openvibers/openvibe.contracts Commits

0.257.0: Your AI — a person's own keys and routing (L36) (#268)

OP OpenVibers committed 13 days ago · 5d0e46ee0b · 72 files, +1885 −85

Changes

ddiff --git a/CHANGELOG.md b/CHANGELOG.md
iindex 6d16ff34..d19c24da 100644
---- a/CHANGELOG.md
++++ b/CHANGELOG.md
@@@ -1,5 +1,26 @@
# Changelog
++## 0.257.0 — 2026-10-31
++
++- **"Your AI": a person's own provider keys, and how their runs route** (L36 Y1/Y3/Y8; OpenVibe.AI). Five additive
++ v1 contracts and one event:
++ - **`ai.me-credential@1`:** one key as its owner reads it — provider, a four-character hint, base_url, daily budget,
++ status (`ok`, `failing` or `untested`), checked_at, a redacted last_error, models_count and created_at. Never the
++ key.
++ - **`ai.me-credential-put@1`:** `PUT /api/v1/me/credentials/{provider}` for the providers the console knows
++ (Anthropic, OpenAI, Gemini, DeepSeek, Mistral, xAI, Groq, Cerebras, Together, Fireworks, OpenRouter, MiniMax and
++ a custom endpoint). A key is required the first time and when the endpoint changes, and is tested on save.
++ - **`ai.me-credential-test@1`:** the model list (up to five samples) or a one-token request, the balance when the
++ provider reports one, and a redacted error that never carries the key.
++ - **`ai.me-credential-list@1`:** `GET /api/v1/me/credentials`, every key without any key.
++ - **`ai.me-preferences@1`:** `GET/PUT /api/v1/me/ai/preferences` — only_my_keys, free_first, a price ceiling and a
++ default model (or `auto`) per kind of work.
++ - **`ai.credential.failed@1`:** a provider refused a person's key with 401/403 during a run; its status becomes
++ failing and the event says so, never the key.
++- **Two person capabilities**, `ai.me.read` and `ai.me.manage`: only the person's own Network token, or the person
++ session on AI's own site behind a same-origin check, reaches them. A service token is refused, and so is any token
++ carrying `on_behalf_of`, whatever capabilities it holds (the AI #204/#205 rule).
++
## 0.256.0 — 2026-10-31
- **`ai.looks` is retired** (`network.consent@1`). The owner removed the rule that AI text never describes people
ddiff --git a/contracts/ai/me-credential-list.v1.json b/contracts/ai/me-credential-list.v1.json
nnew file mode 100644
iindex 00000000..70a19d97
---- /dev/null
++++ b/contracts/ai/me-credential-list.v1.json
@@@ -0,0 +1,20 @@
++{
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/ai/me-credential-list.v1.json",
++ "title": "AiMeCredentialListV1",
++ "description": "ai.me-credential-list@1 (OpenVibe.AI; L36 Y1): GET /api/v1/me/credentials — every provider key the person holds themselves, without any key. Ordered by provider so a page can render it steadily.",
++ "type": "object",
++ "required": [
++ "credentials"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "credentials": {
++ "type": "array",
++ "maxItems": 32,
++ "items": {
++ "$ref": "me-credential.v1.json"
++ }
++ }
++ }
++}
ddiff --git a/contracts/ai/me-credential-put.v1.json b/contracts/ai/me-credential-put.v1.json
nnew file mode 100644
iindex 00000000..c486a869
---- /dev/null
++++ b/contracts/ai/me-credential-put.v1.json
@@@ -0,0 +1,96 @@
++{
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/ai/me-credential-put.v1.json",
++ "title": "AiMeCredentialPutV1",
++ "description": "ai.me-credential-put@1 (OpenVibe.AI; L36 Y1): the person stores or replaces their own provider key at PUT /api/v1/me/credentials/{provider}. Only the person's own Network token or their AI site session may send it. `key` is required the first time and whenever `base_url` changes; a later save may leave it out to keep the stored key. The key is sealed at rest, tested on save and never returned, logged or put in an error.",
++ "type": "object",
++ "required": [
++ "provider"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "provider": {
++ "enum": [
++ "anthropic",
++ "openai",
++ "gemini",
++ "deepseek",
++ "mistral",
++ "xai",
++ "groq",
++ "cerebras",
++ "together",
++ "fireworks",
++ "openrouter",
++ "minimax",
++ "custom"
++ ],
++ "description": "The provider. The route names it too; when both are present they must agree. A custom provider must name its endpoint in base_url."
++ },
++ "key": {
++ "type": "string",
++ "minLength": 8,
++ "maxLength": 400,
++ "description": "The provider key. Required the first time and whenever base_url changes; optional afterwards, when the stored key is kept. Never returned."
++ },
++ "base_url": {
++ "type": "string",
++ "maxLength": 300,
++ "pattern": "^https://[^\\s/?#@]+(/[^\\s?#]*)?$",
++ "description": "The endpoint, for a custom provider or to override the provider's own. https only, no credentials, query or fragment; reached through the guarded fetcher's egress rules."
++ },
++ "budget_usd_per_day": {
++ "type": "number",
++ "minimum": 0,
++ "maximum": 1000,
++ "description": "The person's own daily cap on this key; 0 or absent means no cap."
++ },
++ "models": {
++ "type": "object",
++ "additionalProperties": false,
++ "maxProperties": 4,
++ "properties": {
++ "chat": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 120
++ },
++ "code": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 120
++ },
++ "vision": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 120
++ },
++ "image": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 120
++ }
++ },
++ "description": "The model this key should use per kind of work. `auto` is not a model: absent means the provider's default, and the person's ai.me-preferences choose between keys."
++ }
++ },
++ "allOf": [
++ {
++ "if": {
++ "properties": {
++ "provider": {
++ "const": "custom"
++ }
++ },
++ "required": [
++ "provider"
++ ]
++ },
++ "then": {
++ "required": [
++ "base_url"
++ ]
++ }
++ }
++ ]
++}
ddiff --git a/contracts/ai/me-credential-test.v1.json b/contracts/ai/me-credential-test.v1.json
nnew file mode 100644
iindex 00000000..5b97e231
---- /dev/null
++++ b/contracts/ai/me-credential-test.v1.json
@@@ -0,0 +1,88 @@
++{
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/ai/me-credential-test.v1.json",
++ "title": "AiMeCredentialTestV1",
++ "description": "ai.me-credential-test@1 (OpenVibe.AI; L36 Y1/Y8): the answer to POST /api/v1/me/credentials/{provider}/test and to the test that runs on save. It lists the provider's models (up to five samples), or, when it lists none, makes a one-token request to its cheapest model. `ok` is false for a refused key, an empty endpoint or a timeout (10 s). `error` is a redacted line that never carries the key. `balance` is present only when the provider reports one.",
++ "type": "object",
++ "required": [
++ "ok",
++ "models_count",
++ "sample_models",
++ "latency_ms",
++ "balance",
++ "error"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "ok": {
++ "type": "boolean"
++ },
++ "models_count": {
++ "type": "integer",
++ "minimum": 0
++ },
++ "sample_models": {
++ "type": "array",
++ "maxItems": 5,
++ "items": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 120
++ }
++ },
++ "latency_ms": {
++ "type": "integer",
++ "minimum": 0
++ },
++ "balance": {
++ "oneOf": [
++ {
++ "type": "null"
++ },
++ {
++ "type": "object",
++ "required": [
++ "amount",
++ "currency"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "amount": {
++ "type": "number"
++ },
++ "currency": {
++ "type": "string",
++ "pattern": "^[A-Z]{3}$"
++ }
++ }
++ }
++ ]
++ },
++ "error": {
++ "oneOf": [
++ {
++ "type": "null"
++ },
++ {
++ "type": "object",
++ "required": [
++ "code",
++ "message"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "code": {
++ "type": "string",
++ "pattern": "^[a-z][a-z0-9_.-]{1,63}$"
++ },
++ "message": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 500
++ }
++ }
++ }
++ ]
++ }
++ }
++}
ddiff --git a/contracts/ai/me-credential.v1.json b/contracts/ai/me-credential.v1.json
nnew file mode 100644
iindex 00000000..0e8e86c3
---- /dev/null
++++ b/contracts/ai/me-credential.v1.json
@@@ -0,0 +1,93 @@
++{
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/ai/me-credential.v1.json",
++ "title": "AiMeCredentialV1",
++ "description": "ai.me-credential@1 (OpenVibe.AI; L36 Y1): one of the person's own provider keys as the person reads it from GET /api/v1/me/credentials. It is never the key: `hint` is the last four characters, and `last_error` is a redacted line that never carries the key. `status` is what the last save-time or run-time check found: ok, failing or untested. `models_count` is how many models the provider listed when it was last tested. Own keys are free to their owner: no fee, no cap and no network budget.",
++ "type": "object",
++ "required": [
++ "provider",
++ "hint",
++ "base_url",
++ "budget_usd_per_day",
++ "status",
++ "checked_at",
++ "last_error",
++ "models_count",
++ "created_at"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "provider": {
++ "enum": [
++ "anthropic",
++ "openai",
++ "gemini",
++ "deepseek",
++ "mistral",
++ "xai",
++ "groq",
++ "cerebras",
++ "together",
++ "fireworks",
++ "openrouter",
++ "minimax",
++ "custom"
++ ]
++ },
++ "hint": {
++ "type": "string",
++ "pattern": "^[\\s\\S]{4}$",
++ "description": "The key's last four characters, so the person can tell two keys apart. Never more."
++ },
++ "base_url": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "maxLength": 300,
++ "description": "The endpoint the person chose, or null for the provider's own (taken from the console presets)."
++ },
++ "budget_usd_per_day": {
++ "type": [
++ "number",
++ "null"
++ ],
++ "minimum": 0,
++ "description": "The person's own daily cap on this key; null or 0 means no cap. The network's budgets never apply to their own key."
++ },
++ "status": {
++ "enum": [
++ "ok",
++ "failing",
++ "untested"
++ ]
++ },
++ "checked_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time"
++ },
++ "last_error": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "maxLength": 500,
++ "description": "Why the key last failed, with the key and any other secret redacted. Never the key itself."
++ },
++ "models_count": {
++ "type": "integer",
++ "minimum": 0
++ },
++ "created_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "updated_at": {
++ "type": "string",
++ "format": "date-time"
++ }
++ }
++}
ddiff --git a/contracts/ai/me-preferences.v1.json b/contracts/ai/me-preferences.v1.json
nnew file mode 100644
iindex 00000000..1cafceb4
---- /dev/null
++++ b/contracts/ai/me-preferences.v1.json
@@@ -0,0 +1,69 @@
++{
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/ai/me-preferences.v1.json",
++ "title": "AiMePreferencesV1",
++ "description": "ai.me-preferences@1 (OpenVibe.AI; L36 Y1/Y3): how one person's AI runs route, stored per person under GET/PUT /api/v1/me/ai/preferences. `only_my_keys` means exactly that: a failed or missing key is never quietly replaced by a paid route. `free_first` puts free tiers ahead of paid ones. `price_ceiling_usd_per_mtok` caps the network's paid fallback per million tokens; the person's own keys are never capped. Each default is a model id or `auto`.",
++ "type": "object",
++ "required": [
++ "only_my_keys",
++ "free_first",
++ "price_ceiling_usd_per_mtok",
++ "defaults"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "only_my_keys": {
++ "type": "boolean"
++ },
++ "free_first": {
++ "type": "boolean"
++ },
++ "price_ceiling_usd_per_mtok": {
++ "type": [
++ "number",
++ "null"
++ ],
++ "minimum": 0,
++ "maximum": 1000
++ },
++ "defaults": {
++ "type": "object",
++ "required": [
++ "text",
++ "code",
++ "vision",
++ "image"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "text": {
++ "$ref": "#/$defs/modelChoice"
++ },
++ "code": {
++ "$ref": "#/$defs/modelChoice"
++ },
++ "vision": {
++ "$ref": "#/$defs/modelChoice"
++ },
++ "image": {
++ "$ref": "#/$defs/modelChoice"
++ }
++ }
++ },
++ "updated_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time"
++ }
++ },
++ "$defs": {
++ "modelChoice": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 120,
++ "description": "A model id, or `auto` to let placement choose."
++ }
++ }
++}
ddiff --git a/contracts/catalog.json b/contracts/catalog.json
iindex a32d01af..0829fbd1 100644
---- a/contracts/catalog.json
++++ b/contracts/catalog.json
@@@ -8345,6 +8345,60 @@
"visibility": "first-party",
"compatibility": "backward",
"status": "active"
++ },
++ {
++ "id": "ai.me-credential",
++ "version": "1.0.0",
++ "owner": "ai",
++ "schema": "ai/me-credential.v1.json",
++ "visibility": "first-party",
++ "compatibility": "backward",
++ "status": "active"
++ },
++ {
++ "id": "ai.me-credential-put",
++ "version": "1.0.0",
++ "owner": "ai",
++ "schema": "ai/me-credential-put.v1.json",
++ "visibility": "first-party",
++ "compatibility": "backward",
++ "status": "active"
++ },
++ {
++ "id": "ai.me-credential-test",
++ "version": "1.0.0",
++ "owner": "ai",
++ "schema": "ai/me-credential-test.v1.json",
++ "visibility": "first-party",
++ "compatibility": "backward",
++ "status": "active"
++ },
++ {
++ "id": "ai.me-credential-list",
++ "version": "1.0.0",
++ "owner": "ai",
++ "schema": "ai/me-credential-list.v1.json",
++ "visibility": "first-party",
++ "compatibility": "backward",
++ "status": "active"
++ },
++ {
++ "id": "ai.me-preferences",
++ "version": "1.0.0",
++ "owner": "ai",
++ "schema": "ai/me-preferences.v1.json",
++ "visibility": "first-party",
++ "compatibility": "backward",
++ "status": "active"
++ },
++ {
++ "id": "ai.credential.failed",
++ "version": "1.0.0",
++ "owner": "ai",
++ "schema": "events/payloads/ai.credential.failed.v1.json",
++ "visibility": "first-party",
++ "compatibility": "backward",
++ "status": "active"
}
]
}
ddiff --git a/contracts/events/payloads/ai.credential.failed.v1.json b/contracts/events/payloads/ai.credential.failed.v1.json
nnew file mode 100644
iindex 00000000..e0876e0b
---- /dev/null
++++ b/contracts/events/payloads/ai.credential.failed.v1.json
@@@ -0,0 +1,51 @@
++{
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/events/payloads/ai.credential.failed.v1.json",
++ "title": "AiCredentialFailedPayload",
++ "description": "ai.credential.failed v1 (OpenVibe.AI; L36 Y8): a provider refused one of a person's own keys with 401 or 403 during a run. The key's status becomes failing and this event tells the products that may show it. `error` is a redacted line that never carries the key. Envelope: on_behalf_of the person, subject { type: credential, id: <subject>:<provider> }, visibility internal, priority important, actor service:ai.",
++ "type": "object",
++ "required": [
++ "subject",
++ "provider",
++ "status",
++ "checked_at",
++ "error"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "subject": {
++ "type": "string",
++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"
++ },
++ "provider": {
++ "enum": [
++ "anthropic",
++ "openai",
++ "gemini",
++ "deepseek",
++ "mistral",
++ "xai",
++ "groq",
++ "cerebras",
++ "together",
++ "fireworks",
++ "openrouter",
++ "minimax",
++ "custom"
++ ]
++ },
++ "status": {
++ "const": "failing"
++ },
++ "checked_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "error": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 500,
++ "description": "The redacted reason. Never the key."
++ }
++ }
++}
ddiff --git a/fixtures/ai.credential.failed/invalid/leaks-key.json b/fixtures/ai.credential.failed/invalid/leaks-key.json
nnew file mode 100644
iindex 00000000..61e07593
---- /dev/null
++++ b/fixtures/ai.credential.failed/invalid/leaks-key.json
@@@ -0,0 +1 @@
++{"subject":"usr_01JAB2C3D4E5F6G7H8J9K0MNPQ","provider":"anthropic","status":"failing","checked_at":"2026-10-31T09:00:00.000Z","error":"401 for sk-ant-EXAMPLE","key":"sk-ant-EXAMPLE"}
ddiff --git a/fixtures/ai.credential.failed/valid/refused.json b/fixtures/ai.credential.failed/valid/refused.json
nnew file mode 100644
iindex 00000000..d7a9d825
---- /dev/null
++++ b/fixtures/ai.credential.failed/valid/refused.json
@@@ -0,0 +1 @@
++{"subject":"usr_01JAB2C3D4E5F6G7H8J9K0MNPQ","provider":"anthropic","status":"failing","checked_at":"2026-10-31T09:00:00.000Z","error":"the provider refused the key (401)"}
ddiff --git a/fixtures/ai.me-credential-list/invalid/leaks-key.json b/fixtures/ai.me-credential-list/invalid/leaks-key.json
nnew file mode 100644
iindex 00000000..494c6d4e
---- /dev/null
++++ b/fixtures/ai.me-credential-list/invalid/leaks-key.json
@@@ -0,0 +1 @@
++{"credentials":[{"provider":"openai","hint":"a1b2","api_key":"sk-EXAMPLE","base_url":null,"budget_usd_per_day":null,"status":"ok","checked_at":null,"last_error":null,"models_count":1,"created_at":"2026-10-31T08:59:00.000Z"}]}
ddiff --git a/fixtures/ai.me-credential-list/valid/two.json b/fixtures/ai.me-credential-list/valid/two.json
nnew file mode 100644
iindex 00000000..c1b43330
---- /dev/null
++++ b/fixtures/ai.me-credential-list/valid/two.json
@@@ -0,0 +1 @@
++{"credentials":[{"provider":"anthropic","hint":"a1b2","base_url":null,"budget_usd_per_day":5,"status":"ok","checked_at":"2026-10-31T09:00:00.000Z","last_error":null,"models_count":12,"created_at":"2026-10-31T08:59:00.000Z"},{"provider":"gemini","hint":"ZZ99","base_url":null,"budget_usd_per_day":null,"status":"failing","checked_at":"2026-10-31T09:01:00.000Z","last_error":"the provider refused the key (401)","models_count":0,"created_at":"2026-10-31T08:58:00.000Z"}]}
ddiff --git a/fixtures/ai.me-credential-put/invalid/custom-without-base.json b/fixtures/ai.me-credential-put/invalid/custom-without-base.json
nnew file mode 100644
iindex 00000000..e6546bf0
---- /dev/null
++++ b/fixtures/ai.me-credential-put/invalid/custom-without-base.json
@@@ -0,0 +1 @@
++{"provider":"custom","key":"sk-test-custom-0123456789"}
ddiff --git a/fixtures/ai.me-credential-put/invalid/http-base.json b/fixtures/ai.me-credential-put/invalid/http-base.json
nnew file mode 100644
iindex 00000000..47c2006c
---- /dev/null
++++ b/fixtures/ai.me-credential-put/invalid/http-base.json
@@@ -0,0 +1 @@
++{"provider":"custom","key":"sk-test-custom-0123456789","base_url":"http://llm.example.com/v1"}
ddiff --git a/fixtures/ai.me-credential-put/valid/anthropic.json b/fixtures/ai.me-credential-put/valid/anthropic.json
nnew file mode 100644
iindex 00000000..82a5e623
---- /dev/null
++++ b/fixtures/ai.me-credential-put/valid/anthropic.json
@@@ -0,0 +1 @@
++{"provider":"anthropic","key":"sk-ant-test-0123456789","budget_usd_per_day":2,"models":{"chat":"claude-haiku-5-5","vision":"claude-sonnet-5-5"}}
ddiff --git a/fixtures/ai.me-credential-put/valid/custom.json b/fixtures/ai.me-credential-put/valid/custom.json
nnew file mode 100644
iindex 00000000..71f08fda
---- /dev/null
++++ b/fixtures/ai.me-credential-put/valid/custom.json
@@@ -0,0 +1 @@
++{"provider":"custom","key":"sk-test-custom-0123456789","base_url":"https://llm.example.com/v1"}
ddiff --git a/fixtures/ai.me-credential-test/invalid/negative-latency.json b/fixtures/ai.me-credential-test/invalid/negative-latency.json
nnew file mode 100644
iindex 00000000..c757e2db
---- /dev/null
++++ b/fixtures/ai.me-credential-test/invalid/negative-latency.json
@@@ -0,0 +1 @@
++{"ok":true,"models_count":1,"sample_models":["m"],"latency_ms":-1,"balance":null,"error":null}
ddiff --git a/fixtures/ai.me-credential-test/valid/ok.json b/fixtures/ai.me-credential-test/valid/ok.json
nnew file mode 100644
iindex 00000000..4832c97b
---- /dev/null
++++ b/fixtures/ai.me-credential-test/valid/ok.json
@@@ -0,0 +1 @@
++{"ok":true,"models_count":3,"sample_models":["gpt-4o-mini","gpt-4o","o4-mini"],"latency_ms":184,"balance":null,"error":null}
ddiff --git a/fixtures/ai.me-credential-test/valid/refused.json b/fixtures/ai.me-credential-test/valid/refused.json
nnew file mode 100644
iindex 00000000..8b289439
---- /dev/null
++++ b/fixtures/ai.me-credential-test/valid/refused.json
@@@ -0,0 +1 @@
++{"ok":false,"models_count":0,"sample_models":[],"latency_ms":42,"balance":{"amount":1.25,"currency":"USD"},"error":{"code":"provider.auth","message":"the provider refused the key (401)"}}
ddiff --git a/fixtures/ai.me-credential/invalid/five-char-hint.json b/fixtures/ai.me-credential/invalid/five-char-hint.json
nnew file mode 100644
iindex 00000000..992571b0
---- /dev/null
++++ b/fixtures/ai.me-credential/invalid/five-char-hint.json
@@@ -0,0 +1 @@
++{"provider":"openai","hint":"…a1b2","base_url":null,"budget_usd_per_day":null,"status":"ok","checked_at":null,"last_error":null,"models_count":1,"created_at":"2026-10-31T08:59:00.000Z"}
ddiff --git a/fixtures/ai.me-credential/invalid/leaks-key.json b/fixtures/ai.me-credential/invalid/leaks-key.json
nnew file mode 100644
iindex 00000000..f5061d75
---- /dev/null
++++ b/fixtures/ai.me-credential/invalid/leaks-key.json
@@@ -0,0 +1 @@
++{"provider":"openai","hint":"a1b2","key":"sk-EXAMPLE","base_url":null,"budget_usd_per_day":null,"status":"ok","checked_at":null,"last_error":null,"models_count":1,"created_at":"2026-10-31T08:59:00.000Z"}
ddiff --git a/fixtures/ai.me-credential/valid/anthropic.json b/fixtures/ai.me-credential/valid/anthropic.json
nnew file mode 100644
iindex 00000000..34391ca3
---- /dev/null
++++ b/fixtures/ai.me-credential/valid/anthropic.json
@@@ -0,0 +1 @@
++{"provider":"anthropic","hint":"a1b2","base_url":null,"budget_usd_per_day":5,"status":"ok","checked_at":"2026-10-31T09:00:00.000Z","last_error":null,"models_count":12,"created_at":"2026-10-31T08:59:00.000Z","updated_at":"2026-10-31T09:00:00.000Z"}
ddiff --git a/fixtures/ai.me-credential/valid/custom-untested.json b/fixtures/ai.me-credential/valid/custom-untested.json
nnew file mode 100644
iindex 00000000..e7bfdc1c
---- /dev/null
++++ b/fixtures/ai.me-credential/valid/custom-untested.json
@@@ -0,0 +1 @@
++{"provider":"custom","hint":"ZZ99","base_url":"https://llm.example.com/v1","budget_usd_per_day":null,"status":"untested","checked_at":null,"last_error":null,"models_count":0,"created_at":"2026-10-31T08:59:00.000Z"}
ddiff --git a/fixtures/ai.me-preferences/invalid/missing-image.json b/fixtures/ai.me-preferences/invalid/missing-image.json
nnew file mode 100644
iindex 00000000..2bf41ee5
---- /dev/null
++++ b/fixtures/ai.me-preferences/invalid/missing-image.json
@@@ -0,0 +1 @@
++{"only_my_keys":false,"free_first":false,"price_ceiling_usd_per_mtok":null,"defaults":{"text":"auto","code":"auto","vision":"auto"}}
ddiff --git a/fixtures/ai.me-preferences/valid/defaults.json b/fixtures/ai.me-preferences/valid/defaults.json
nnew file mode 100644
iindex 00000000..076d3958
---- /dev/null
++++ b/fixtures/ai.me-preferences/valid/defaults.json
@@@ -0,0 +1 @@
++{"only_my_keys":true,"free_first":true,"price_ceiling_usd_per_mtok":5,"defaults":{"text":"auto","code":"claude-sonnet-5-5","vision":"auto","image":"auto"},"updated_at":"2026-10-31T09:00:00.000Z"}
ddiff --git a/generated/json-schema/bundle.json b/generated/json-schema/bundle.json
iindex 67ff637d..98b4c7fe 100644
---- a/generated/json-schema/bundle.json
++++ b/generated/json-schema/bundle.json
@@@ -93681,6 +93681,453 @@
}
}
}
++ },
++ "ai.me-credential": {
++ "version": "1.0.0",
++ "owner": "ai",
++ "status": "active",
++ "schema": {
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/ai/me-credential.v1.json",
++ "title": "AiMeCredentialV1",
++ "description": "ai.me-credential@1 (OpenVibe.AI; L36 Y1): one of the person's own provider keys as the person reads it from GET /api/v1/me/credentials. It is never the key: `hint` is the last four characters, and `last_error` is a redacted line that never carries the key. `status` is what the last save-time or run-time check found: ok, failing or untested. `models_count` is how many models the provider listed when it was last tested. Own keys are free to their owner: no fee, no cap and no network budget.",
++ "type": "object",
++ "required": [
++ "provider",
++ "hint",
++ "base_url",
++ "budget_usd_per_day",
++ "status",
++ "checked_at",
++ "last_error",
++ "models_count",
++ "created_at"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "provider": {
++ "enum": [
++ "anthropic",
++ "openai",
++ "gemini",
++ "deepseek",
++ "mistral",
++ "xai",
++ "groq",
++ "cerebras",
++ "together",
++ "fireworks",
++ "openrouter",
++ "minimax",
++ "custom"
++ ]
++ },
++ "hint": {
++ "type": "string",
++ "pattern": "^[\\s\\S]{4}$",
++ "description": "The key's last four characters, so the person can tell two keys apart. Never more."
++ },
++ "base_url": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "maxLength": 300,
++ "description": "The endpoint the person chose, or null for the provider's own (taken from the console presets)."
++ },
++ "budget_usd_per_day": {
++ "type": [
++ "number",
++ "null"
++ ],
++ "minimum": 0,
++ "description": "The person's own daily cap on this key; null or 0 means no cap. The network's budgets never apply to their own key."
++ },
++ "status": {
++ "enum": [
++ "ok",
++ "failing",
++ "untested"
++ ]
++ },
++ "checked_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time"
++ },
++ "last_error": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "maxLength": 500,
++ "description": "Why the key last failed, with the key and any other secret redacted. Never the key itself."
++ },
++ "models_count": {
++ "type": "integer",
++ "minimum": 0
++ },
++ "created_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "updated_at": {
++ "type": "string",
++ "format": "date-time"
++ }
++ }
++ }
++ },
++ "ai.me-credential-put": {
++ "version": "1.0.0",
++ "owner": "ai",
++ "status": "active",
++ "schema": {
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/ai/me-credential-put.v1.json",
++ "title": "AiMeCredentialPutV1",
++ "description": "ai.me-credential-put@1 (OpenVibe.AI; L36 Y1): the person stores or replaces their own provider key at PUT /api/v1/me/credentials/{provider}. Only the person's own Network token or their AI site session may send it. `key` is required the first time and whenever `base_url` changes; a later save may leave it out to keep the stored key. The key is sealed at rest, tested on save and never returned, logged or put in an error.",
++ "type": "object",
++ "required": [
++ "provider"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "provider": {
++ "enum": [
++ "anthropic",
++ "openai",
++ "gemini",
++ "deepseek",
++ "mistral",
++ "xai",
++ "groq",
++ "cerebras",
++ "together",
++ "fireworks",
++ "openrouter",
++ "minimax",
++ "custom"
++ ],
++ "description": "The provider. The route names it too; when both are present they must agree. A custom provider must name its endpoint in base_url."
++ },
++ "key": {
++ "type": "string",
++ "minLength": 8,
++ "maxLength": 400,
++ "description": "The provider key. Required the first time and whenever base_url changes; optional afterwards, when the stored key is kept. Never returned."
++ },
++ "base_url": {
++ "type": "string",
++ "maxLength": 300,
++ "pattern": "^https://[^\\s/?#@]+(/[^\\s?#]*)?$",
++ "description": "The endpoint, for a custom provider or to override the provider's own. https only, no credentials, query or fragment; reached through the guarded fetcher's egress rules."
++ },
++ "budget_usd_per_day": {
++ "type": "number",
++ "minimum": 0,
++ "maximum": 1000,
++ "description": "The person's own daily cap on this key; 0 or absent means no cap."
++ },
++ "models": {
++ "type": "object",
++ "additionalProperties": false,
++ "maxProperties": 4,
++ "properties": {
++ "chat": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 120
++ },
++ "code": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 120
++ },
++ "vision": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 120
++ },
++ "image": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 120
++ }
++ },
++ "description": "The model this key should use per kind of work. `auto` is not a model: absent means the provider's default, and the person's ai.me-preferences choose between keys."
++ }
++ },
++ "allOf": [
++ {
++ "if": {
++ "properties": {
++ "provider": {
++ "const": "custom"
++ }
++ },
++ "required": [
++ "provider"
++ ]
++ },
++ "then": {
++ "required": [
++ "base_url"
++ ]
++ }
++ }
++ ]
++ }
++ },
++ "ai.me-credential-test": {
++ "version": "1.0.0",
++ "owner": "ai",
++ "status": "active",
++ "schema": {
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/ai/me-credential-test.v1.json",
++ "title": "AiMeCredentialTestV1",
++ "description": "ai.me-credential-test@1 (OpenVibe.AI; L36 Y1/Y8): the answer to POST /api/v1/me/credentials/{provider}/test and to the test that runs on save. It lists the provider's models (up to five samples), or, when it lists none, makes a one-token request to its cheapest model. `ok` is false for a refused key, an empty endpoint or a timeout (10 s). `error` is a redacted line that never carries the key. `balance` is present only when the provider reports one.",
++ "type": "object",
++ "required": [
++ "ok",
++ "models_count",
++ "sample_models",
++ "latency_ms",
++ "balance",
++ "error"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "ok": {
++ "type": "boolean"
++ },
++ "models_count": {
++ "type": "integer",
++ "minimum": 0
++ },
++ "sample_models": {
++ "type": "array",
++ "maxItems": 5,
++ "items": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 120
++ }
++ },
++ "latency_ms": {
++ "type": "integer",
++ "minimum": 0
++ },
++ "balance": {
++ "oneOf": [
++ {
++ "type": "null"
++ },
++ {
++ "type": "object",
++ "required": [
++ "amount",
++ "currency"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "amount": {
++ "type": "number"
++ },
++ "currency": {
++ "type": "string",
++ "pattern": "^[A-Z]{3}$"
++ }
++ }
++ }
++ ]
++ },
++ "error": {
++ "oneOf": [
++ {
++ "type": "null"
++ },
++ {
++ "type": "object",
++ "required": [
++ "code",
++ "message"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "code": {
++ "type": "string",
++ "pattern": "^[a-z][a-z0-9_.-]{1,63}$"
++ },
++ "message": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 500
++ }
++ }
++ }
++ ]
++ }
++ }
++ }
++ },
++ "ai.me-credential-list": {
++ "version": "1.0.0",
++ "owner": "ai",
++ "status": "active",
++ "schema": {
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/ai/me-credential-list.v1.json",
++ "title": "AiMeCredentialListV1",
++ "description": "ai.me-credential-list@1 (OpenVibe.AI; L36 Y1): GET /api/v1/me/credentials — every provider key the person holds themselves, without any key. Ordered by provider so a page can render it steadily.",
++ "type": "object",
++ "required": [
++ "credentials"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "credentials": {
++ "type": "array",
++ "maxItems": 32,
++ "items": {
++ "$ref": "me-credential.v1.json"
++ }
++ }
++ }
++ }
++ },
++ "ai.me-preferences": {
++ "version": "1.0.0",
++ "owner": "ai",
++ "status": "active",
++ "schema": {
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/ai/me-preferences.v1.json",
++ "title": "AiMePreferencesV1",
++ "description": "ai.me-preferences@1 (OpenVibe.AI; L36 Y1/Y3): how one person's AI runs route, stored per person under GET/PUT /api/v1/me/ai/preferences. `only_my_keys` means exactly that: a failed or missing key is never quietly replaced by a paid route. `free_first` puts free tiers ahead of paid ones. `price_ceiling_usd_per_mtok` caps the network's paid fallback per million tokens; the person's own keys are never capped. Each default is a model id or `auto`.",
++ "type": "object",
++ "required": [
++ "only_my_keys",
++ "free_first",
++ "price_ceiling_usd_per_mtok",
++ "defaults"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "only_my_keys": {
++ "type": "boolean"
++ },
++ "free_first": {
++ "type": "boolean"
++ },
++ "price_ceiling_usd_per_mtok": {
++ "type": [
++ "number",
++ "null"
++ ],
++ "minimum": 0,
++ "maximum": 1000
++ },
++ "defaults": {
++ "type": "object",
++ "required": [
++ "text",
++ "code",
++ "vision",
++ "image"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "text": {
++ "$ref": "#/$defs/modelChoice"
++ },
++ "code": {
++ "$ref": "#/$defs/modelChoice"
++ },
++ "vision": {
++ "$ref": "#/$defs/modelChoice"
++ },
++ "image": {
++ "$ref": "#/$defs/modelChoice"
++ }
++ }
++ },
++ "updated_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time"
++ }
++ },
++ "$defs": {
++ "modelChoice": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 120,
++ "description": "A model id, or `auto` to let placement choose."
++ }
++ }
++ }
++ },
++ "ai.credential.failed": {
++ "version": "1.0.0",
++ "owner": "ai",
++ "status": "active",
++ "schema": {
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/events/payloads/ai.credential.failed.v1.json",
++ "title": "AiCredentialFailedPayload",
++ "description": "ai.credential.failed v1 (OpenVibe.AI; L36 Y8): a provider refused one of a person's own keys with 401 or 403 during a run. The key's status becomes failing and this event tells the products that may show it. `error` is a redacted line that never carries the key. Envelope: on_behalf_of the person, subject { type: credential, id: <subject>:<provider> }, visibility internal, priority important, actor service:ai.",
++ "type": "object",
++ "required": [
++ "subject",
++ "provider",
++ "status",
++ "checked_at",
++ "error"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "subject": {
++ "type": "string",
++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"
++ },
++ "provider": {
++ "enum": [
++ "anthropic",
++ "openai",
++ "gemini",
++ "deepseek",
++ "mistral",
++ "xai",
++ "groq",
++ "cerebras",
++ "together",
++ "fireworks",
++ "openrouter",
++ "minimax",
++ "custom"
++ ]
++ },
++ "status": {
++ "const": "failing"
++ },
++ "checked_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "error": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 500,
++ "description": "The redacted reason. Never the key."
++ }
++ }
++ }
}
}
}
ddiff --git a/generated/openapi/actor.json b/generated/openapi/actor.json
iindex 17583c80..2097034b 100644
---- a/generated/openapi/actor.json
++++ b/generated/openapi/actor.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Actor API",
-- "version": "0.256.0",
-- "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
ddiff --git a/generated/openapi/ai.json b/generated/openapi/ai.json
iindex 82adb600..220f634c 100644
---- a/generated/openapi/ai.json
++++ b/generated/openapi/ai.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.AI API",
-- "version": "0.256.0",
-- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
@@@ -1133,6 +1133,411 @@
}
}
},
++ "/api/v1/me/ai/preferences": {
++ "get": {
++ "operationId": "get_api_v1_me_ai_preferences",
++ "summary": "The person's own AI settings (L36 Y1): GET /api/v1/me/credentials lists the provider keys they hold themselves, never a key (a four-character hint only), and GET /api/v1/me/ai/preferences reads how th",
++ "description": "**ai.me.read** (public): The person's own AI settings (L36 Y1): GET /api/v1/me/credentials lists the provider keys they hold themselves, never a key (a four-character hint only), and GET /api/v1/me/ai/preferences reads how their runs route. Only the person may call it — their Network user token, or the person session on AI's own site behind a same-origin check. A service token is refused, and so is any token carrying on_behalf_of, whatever capabilities it holds.",
++ "tags": [
++ "ai.me"
++ ],
++ "x-openvibe-capabilities": [
++ "ai.me.read"
++ ],
++ "x-openvibe-visibility": [
++ "public"
++ ],
++ "x-openvibe-quota-class": "ai-credentials",
++ "security": [
++ {
++ "openvibeToken": [
++ "ai.me.read"
++ ]
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/ai.me-credential-list.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_me_ai_preferences",
++ "summary": "The person stores, tests and deletes their own provider keys and chooses how their runs route (L36 Y1/Y3): PUT/DELETE /api/v1/me/credentials/{provider}, POST /api/v1/me/credentials/{provider}/test and",
++ "description": "**ai.me.manage** (public): The person stores, tests and deletes their own provider keys and chooses how their runs route (L36 Y1/Y3): PUT/DELETE /api/v1/me/credentials/{provider}, POST /api/v1/me/credentials/{provider}/test and PUT /api/v1/me/ai/preferences. A key is tested on save (models, or a one-token request), sealed at rest and never returned, logged or put in an error. Only the person may call it — a service token or any token carrying on_behalf_of is refused, whatever capabilities it holds.",
++ "tags": [
++ "ai.me"
++ ],
++ "x-openvibe-capabilities": [
++ "ai.me.manage"
++ ],
++ "x-openvibe-visibility": [
++ "public"
++ ],
++ "x-openvibe-quota-class": "ai-credentials",
++ "security": [
++ {
++ "openvibeToken": [
++ "ai.me.manage"
++ ]
++ }
++ ],
++ "requestBody": {
++ "required": false,
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/ai.me-credential-put.v1"
++ }
++ }
++ }
++ },
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/ai.me-credential.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ }
++ },
++ "/api/v1/me/credentials": {
++ "get": {
++ "operationId": "get_api_v1_me_credentials",
++ "summary": "The person's own AI settings (L36 Y1): GET /api/v1/me/credentials lists the provider keys they hold themselves, never a key (a four-character hint only), and GET /api/v1/me/ai/preferences reads how th",
++ "description": "**ai.me.read** (public): The person's own AI settings (L36 Y1): GET /api/v1/me/credentials lists the provider keys they hold themselves, never a key (a four-character hint only), and GET /api/v1/me/ai/preferences reads how their runs route. Only the person may call it — their Network user token, or the person session on AI's own site behind a same-origin check. A service token is refused, and so is any token carrying on_behalf_of, whatever capabilities it holds.",
++ "tags": [
++ "ai.me"
++ ],
++ "x-openvibe-capabilities": [
++ "ai.me.read"
++ ],
++ "x-openvibe-visibility": [
++ "public"
++ ],
++ "x-openvibe-quota-class": "ai-credentials",
++ "security": [
++ {
++ "openvibeToken": [
++ "ai.me.read"
++ ]
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/ai.me-credential-list.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ }
++ },
++ "/api/v1/me/credentials/{provider}": {
++ "delete": {
++ "operationId": "delete_api_v1_me_credentials_provider",
++ "summary": "The person stores, tests and deletes their own provider keys and chooses how their runs route (L36 Y1/Y3): PUT/DELETE /api/v1/me/credentials/{provider}, POST /api/v1/me/credentials/{provider}/test and",
++ "description": "**ai.me.manage** (public): The person stores, tests and deletes their own provider keys and chooses how their runs route (L36 Y1/Y3): PUT/DELETE /api/v1/me/credentials/{provider}, POST /api/v1/me/credentials/{provider}/test and PUT /api/v1/me/ai/preferences. A key is tested on save (models, or a one-token request), sealed at rest and never returned, logged or put in an error. Only the person may call it — a service token or any token carrying on_behalf_of is refused, whatever capabilities it holds.",
++ "tags": [
++ "ai.me"
++ ],
++ "x-openvibe-capabilities": [
++ "ai.me.manage"
++ ],
++ "x-openvibe-visibility": [
++ "public"
++ ],
++ "x-openvibe-quota-class": "ai-credentials",
++ "security": [
++ {
++ "openvibeToken": [
++ "ai.me.manage"
++ ]
++ }
++ ],
++ "x-openvibe-input": {
++ "$ref": "#/components/schemas/ai.me-credential-put.v1"
++ },
++ "parameters": [
++ {
++ "name": "provider",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ },
++ {
++ "name": "key",
++ "in": "query",
++ "required": false,
++ "schema": {
++ "type": "string",
++ "minLength": 8,
++ "maxLength": 400,
++ "description": "The provider key. Required the first time and whenever base_url changes; optional afterwards, when the stored key is kept. Never returned."
++ },
++ "description": "The provider key. Required the first time and whenever base_url changes; optional afterwards, when the stored key is kept. Never returned."
++ },
++ {
++ "name": "base_url",
++ "in": "query",
++ "required": false,
++ "schema": {
++ "type": "string",
++ "maxLength": 300,
++ "pattern": "^https://[^\\s/?#@]+(/[^\\s?#]*)?$",
++ "description": "The endpoint, for a custom provider or to override the provider's own. https only, no credentials, query or fragment; reached through the guarded fetcher's egress rules."
++ },
++ "description": "The endpoint, for a custom provider or to override the provider's own. https only, no credentials, query or fragment; reached through the guarded fetcher's egress rules."
++ },
++ {
++ "name": "budget_usd_per_day",
++ "in": "query",
++ "required": false,
++ "schema": {
++ "type": "number",
++ "minimum": 0,
++ "maximum": 1000,
++ "description": "The person's own daily cap on this key; 0 or absent means no cap."
++ },
++ "description": "The person's own daily cap on this key; 0 or absent means no cap."
++ },
++ {
++ "name": "models",
++ "in": "query",
++ "required": false,
++ "schema": {
++ "type": "object",
++ "additionalProperties": false,
++ "maxProperties": 4,
++ "properties": {
++ "chat": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 120
++ },
++ "code": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 120
++ },
++ "vision": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 120
++ },
++ "image": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 120
++ }
++ },
++ "description": "The model this key should use per kind of work. `auto` is not a model: absent means the provider's default, and the person's ai.me-preferences choose between keys."
++ },
++ "description": "The model this key should use per kind of work. `auto` is not a model: absent means the provider's default, and the person's ai.me-preferences choose between keys."
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/ai.me-credential.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_me_credentials_provider",
++ "summary": "The person stores, tests and deletes their own provider keys and chooses how their runs route (L36 Y1/Y3): PUT/DELETE /api/v1/me/credentials/{provider}, POST /api/v1/me/credentials/{provider}/test and",
++ "description": "**ai.me.manage** (public): The person stores, tests and deletes their own provider keys and chooses how their runs route (L36 Y1/Y3): PUT/DELETE /api/v1/me/credentials/{provider}, POST /api/v1/me/credentials/{provider}/test and PUT /api/v1/me/ai/preferences. A key is tested on save (models, or a one-token request), sealed at rest and never returned, logged or put in an error. Only the person may call it — a service token or any token carrying on_behalf_of is refused, whatever capabilities it holds.",
++ "tags": [
++ "ai.me"
++ ],
++ "x-openvibe-capabilities": [
++ "ai.me.manage"
++ ],
++ "x-openvibe-visibility": [
++ "public"
++ ],
++ "x-openvibe-quota-class": "ai-credentials",
++ "security": [
++ {
++ "openvibeToken": [
++ "ai.me.manage"
++ ]
++ }
++ ],
++ "requestBody": {
++ "required": false,
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/ai.me-credential-put.v1"
++ }
++ }
++ }
++ },
++ "parameters": [
++ {
++ "name": "provider",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/ai.me-credential.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ }
++ },
++ "/api/v1/me/credentials/{provider}/test": {
++ "post": {
++ "operationId": "post_api_v1_me_credentials_provider_test",
++ "summary": "The person stores, tests and deletes their own provider keys and chooses how their runs route (L36 Y1/Y3): PUT/DELETE /api/v1/me/credentials/{provider}, POST /api/v1/me/credentials/{provider}/test and",
++ "description": "**ai.me.manage** (public): The person stores, tests and deletes their own provider keys and chooses how their runs route (L36 Y1/Y3): PUT/DELETE /api/v1/me/credentials/{provider}, POST /api/v1/me/credentials/{provider}/test and PUT /api/v1/me/ai/preferences. A key is tested on save (models, or a one-token request), sealed at rest and never returned, logged or put in an error. Only the person may call it — a service token or any token carrying on_behalf_of is refused, whatever capabilities it holds.",
++ "tags": [
++ "ai.me"
++ ],
++ "x-openvibe-capabilities": [
++ "ai.me.manage"
++ ],
++ "x-openvibe-visibility": [
++ "public"
++ ],
++ "x-openvibe-quota-class": "ai-credentials",
++ "security": [
++ {
++ "openvibeToken": [
++ "ai.me.manage"
++ ]
++ }
++ ],
++ "requestBody": {
++ "required": false,
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/ai.me-credential-put.v1"
++ }
++ }
++ }
++ },
++ "parameters": [
++ {
++ "name": "provider",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/ai.me-credential.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ }
++ },
"/api/v1/memory/items": {
"delete": {
"operationId": "delete_api_v1_memory_items",
@@@ -4206,6 +4611,209 @@
}
]
},
++ "ai.me-credential-list.v1": {
++ "title": "AiMeCredentialListV1",
++ "description": "ai.me-credential-list@1 (OpenVibe.AI; L36 Y1): GET /api/v1/me/credentials — every provider key the person holds themselves, without any key. Ordered by provider so a page can render it steadily.",
++ "type": "object",
++ "required": [
++ "credentials"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "credentials": {
++ "type": "array",
++ "maxItems": 32,
++ "items": {
++ "$ref": "#/components/schemas/ai.me-credential.v1"
++ }
++ }
++ }
++ },
++ "ai.me-credential-put.v1": {
++ "title": "AiMeCredentialPutV1",
++ "description": "ai.me-credential-put@1 (OpenVibe.AI; L36 Y1): the person stores or replaces their own provider key at PUT /api/v1/me/credentials/{provider}. Only the person's own Network token or their AI site session may send it. `key` is required the first time and whenever `base_url` changes; a later save may leave it out to keep the stored key. The key is sealed at rest, tested on save and never returned, logged or put in an error.",
++ "type": "object",
++ "required": [
++ "provider"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "provider": {
++ "enum": [
++ "anthropic",
++ "openai",
++ "gemini",
++ "deepseek",
++ "mistral",
++ "xai",
++ "groq",
++ "cerebras",
++ "together",
++ "fireworks",
++ "openrouter",
++ "minimax",
++ "custom"
++ ],
++ "description": "The provider. The route names it too; when both are present they must agree. A custom provider must name its endpoint in base_url."
++ },
++ "key": {
++ "type": "string",
++ "minLength": 8,
++ "maxLength": 400,
++ "description": "The provider key. Required the first time and whenever base_url changes; optional afterwards, when the stored key is kept. Never returned."
++ },
++ "base_url": {
++ "type": "string",
++ "maxLength": 300,
++ "pattern": "^https://[^\\s/?#@]+(/[^\\s?#]*)?$",
++ "description": "The endpoint, for a custom provider or to override the provider's own. https only, no credentials, query or fragment; reached through the guarded fetcher's egress rules."
++ },
++ "budget_usd_per_day": {
++ "type": "number",
++ "minimum": 0,
++ "maximum": 1000,
++ "description": "The person's own daily cap on this key; 0 or absent means no cap."
++ },
++ "models": {
++ "type": "object",
++ "additionalProperties": false,
++ "maxProperties": 4,
++ "properties": {
++ "chat": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 120
++ },
++ "code": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 120
++ },
++ "vision": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 120
++ },
++ "image": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 120
++ }
++ },
++ "description": "The model this key should use per kind of work. `auto` is not a model: absent means the provider's default, and the person's ai.me-preferences choose between keys."
++ }
++ },
++ "allOf": [
++ {
++ "if": {
++ "properties": {
++ "provider": {
++ "const": "custom"
++ }
++ },
++ "required": [
++ "provider"
++ ]
++ },
++ "then": {
++ "required": [
++ "base_url"
++ ]
++ }
++ }
++ ]
++ },
++ "ai.me-credential.v1": {
++ "title": "AiMeCredentialV1",
++ "description": "ai.me-credential@1 (OpenVibe.AI; L36 Y1): one of the person's own provider keys as the person reads it from GET /api/v1/me/credentials. It is never the key: `hint` is the last four characters, and `last_error` is a redacted line that never carries the key. `status` is what the last save-time or run-time check found: ok, failing or untested. `models_count` is how many models the provider listed when it was last tested. Own keys are free to their owner: no fee, no cap and no network budget.",
++ "type": "object",
++ "required": [
++ "provider",
++ "hint",
++ "base_url",
++ "budget_usd_per_day",
++ "status",
++ "checked_at",
++ "last_error",
++ "models_count",
++ "created_at"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "provider": {
++ "enum": [
++ "anthropic",
++ "openai",
++ "gemini",
++ "deepseek",
++ "mistral",
++ "xai",
++ "groq",
++ "cerebras",
++ "together",
++ "fireworks",
++ "openrouter",
++ "minimax",
++ "custom"
++ ]
++ },
++ "hint": {
++ "type": "string",
++ "pattern": "^[\\s\\S]{4}$",
++ "description": "The key's last four characters, so the person can tell two keys apart. Never more."
++ },
++ "base_url": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "maxLength": 300,
++ "description": "The endpoint the person chose, or null for the provider's own (taken from the console presets)."
++ },
++ "budget_usd_per_day": {
++ "type": [
++ "number",
++ "null"
++ ],
++ "minimum": 0,
++ "description": "The person's own daily cap on this key; null or 0 means no cap. The network's budgets never apply to their own key."
++ },
++ "status": {
++ "enum": [
++ "ok",
++ "failing",
++ "untested"
++ ]
++ },
++ "checked_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time"
++ },
++ "last_error": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "maxLength": 500,
++ "description": "Why the key last failed, with the key and any other secret redacted. Never the key itself."
++ },
++ "models_count": {
++ "type": "integer",
++ "minimum": 0
++ },
++ "created_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "updated_at": {
++ "type": "string",
++ "format": "date-time"
++ }
++ }
++ },
"ai.memory-item.v1": {
"title": "AiMemoryItem",
"description": "ai.memory-item@1 (OpenVibe.AI; Memory M2): one memory item as the person's own API reads it. Tenant is the owner (`user:usr_…`); the text is decrypted only inside AI's process and never logged, never in an error, a span or an event. `source_ref` is the file path relative to the scanned root and the heading, when the item came from a file.",
ddiff --git a/generated/openapi/bench.json b/generated/openapi/bench.json
iindex 516efe9b..de603d29 100644
---- a/generated/openapi/bench.json
++++ b/generated/openapi/bench.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Bench API",
-- "version": "0.256.0",
-- "description": "Routes of OpenVibe.Bench as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenVibe.Bench as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
ddiff --git a/generated/openapi/billing.json b/generated/openapi/billing.json
iindex d420a06b..8ef14b0f 100644
---- a/generated/openapi/billing.json
++++ b/generated/openapi/billing.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Billing API",
-- "version": "0.256.0",
-- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
ddiff --git a/generated/openapi/blog.json b/generated/openapi/blog.json
iindex fd7a7cae..d8c0a5a4 100644
---- a/generated/openapi/blog.json
++++ b/generated/openapi/blog.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Blog API",
-- "version": "0.256.0",
-- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
ddiff --git a/generated/openapi/bot.json b/generated/openapi/bot.json
iindex 08211618..6cf642bb 100644
---- a/generated/openapi/bot.json
++++ b/generated/openapi/bot.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Bot API",
-- "version": "0.256.0",
-- "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
ddiff --git a/generated/openapi/chat.json b/generated/openapi/chat.json
iindex 571a06f0..a0635a82 100644
---- a/generated/openapi/chat.json
++++ b/generated/openapi/chat.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Chat API",
-- "version": "0.256.0",
-- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
ddiff --git a/generated/openapi/codes.json b/generated/openapi/codes.json
iindex f7d249e0..63a23b51 100644
---- a/generated/openapi/codes.json
++++ b/generated/openapi/codes.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Codes API",
-- "version": "0.256.0",
-- "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
ddiff --git a/generated/openapi/community.json b/generated/openapi/community.json
iindex 51d7ee54..b0cd4a9c 100644
---- a/generated/openapi/community.json
++++ b/generated/openapi/community.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Community API",
-- "version": "0.256.0",
-- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
ddiff --git a/generated/openapi/coupons.json b/generated/openapi/coupons.json
iindex 026899bb..242e91c6 100644
---- a/generated/openapi/coupons.json
++++ b/generated/openapi/coupons.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Coupons API",
-- "version": "0.256.0",
-- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
ddiff --git a/generated/openapi/deals.json b/generated/openapi/deals.json
iindex 8d31c601..39af9ad3 100644
---- a/generated/openapi/deals.json
++++ b/generated/openapi/deals.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Deals API",
-- "version": "0.256.0",
-- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
ddiff --git a/generated/openapi/domains.json b/generated/openapi/domains.json
iindex 9b9797e6..784be030 100644
---- a/generated/openapi/domains.json
++++ b/generated/openapi/domains.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Domains API",
-- "version": "0.256.0",
-- "description": "Routes of OpenVibe.Domains as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenVibe.Domains as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
ddiff --git a/generated/openapi/events.json b/generated/openapi/events.json
iindex 62e63e2d..53b587c4 100644
---- a/generated/openapi/events.json
++++ b/generated/openapi/events.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Events API",
-- "version": "0.256.0",
-- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
ddiff --git a/generated/openapi/food.json b/generated/openapi/food.json
iindex 76cf0231..cbcca0f9 100644
---- a/generated/openapi/food.json
++++ b/generated/openapi/food.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Food API",
-- "version": "0.256.0",
-- "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
ddiff --git a/generated/openapi/games.json b/generated/openapi/games.json
iindex b6d868d8..92682df1 100644
---- a/generated/openapi/games.json
++++ b/generated/openapi/games.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Games API",
-- "version": "0.256.0",
-- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
ddiff --git a/generated/openapi/host.json b/generated/openapi/host.json
iindex 31b8bda5..6e8d1ace 100644
---- a/generated/openapi/host.json
++++ b/generated/openapi/host.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Host API",
-- "version": "0.256.0",
-- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
ddiff --git a/generated/openapi/index.json b/generated/openapi/index.json
iindex 5885f57e..dfe0a1b6 100644
---- a/generated/openapi/index.json
++++ b/generated/openapi/index.json
@@@ -12,8 +12,8 @@
"service": "ai",
"name": "OpenVibe.AI",
"origin": "https://ai.openvibe.services",
-- "operations": 52,
-- "capabilities": 11
++ "operations": 58,
++ "capabilities": 13
},
{
"service": "bench",
ddiff --git a/generated/openapi/inventory.json b/generated/openapi/inventory.json
iindex 6ec4e125..77bafba7 100644
---- a/generated/openapi/inventory.json
++++ b/generated/openapi/inventory.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Inventory API",
-- "version": "0.256.0",
-- "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
ddiff --git a/generated/openapi/live.json b/generated/openapi/live.json
iindex cadbed98..c797e1de 100644
---- a/generated/openapi/live.json
++++ b/generated/openapi/live.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Live API",
-- "version": "0.256.0",
-- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
ddiff --git a/generated/openapi/media-hub.json b/generated/openapi/media-hub.json
iindex 2274ac2d..170bcbdd 100644
---- a/generated/openapi/media-hub.json
++++ b/generated/openapi/media-hub.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.MediaHub API",
-- "version": "0.256.0",
-- "description": "Routes of OpenVibe.MediaHub as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenVibe.MediaHub as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
ddiff --git a/generated/openapi/media.json b/generated/openapi/media.json
iindex ec0a4846..8f6f7766 100644
---- a/generated/openapi/media.json
++++ b/generated/openapi/media.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Media API",
-- "version": "0.256.0",
-- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
ddiff --git a/generated/openapi/network.json b/generated/openapi/network.json
iindex 29c1a8de..c68efa1f 100644
---- a/generated/openapi/network.json
++++ b/generated/openapi/network.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Network API",
-- "version": "0.256.0",
-- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
ddiff --git a/generated/openapi/news.json b/generated/openapi/news.json
iindex 202ef217..88dec559 100644
---- a/generated/openapi/news.json
++++ b/generated/openapi/news.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.News API",
-- "version": "0.256.0",
-- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
ddiff --git a/generated/openapi/openre.json b/generated/openapi/openre.json
iindex 78bfd83d..196fd0e7 100644
---- a/generated/openapi/openre.json
++++ b/generated/openapi/openre.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenRestream API",
-- "version": "0.256.0",
-- "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
ddiff --git a/generated/openapi/play.json b/generated/openapi/play.json
iindex ae522d25..087833bd 100644
---- a/generated/openapi/play.json
++++ b/generated/openapi/play.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Play API",
-- "version": "0.256.0",
-- "description": "Routes of OpenVibe.Play as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenVibe.Play as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
ddiff --git a/generated/openapi/quest.json b/generated/openapi/quest.json
iindex 2fab6750..b0417f21 100644
---- a/generated/openapi/quest.json
++++ b/generated/openapi/quest.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Quest API",
-- "version": "0.256.0",
-- "description": "Routes of OpenVibe.Quest as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenVibe.Quest as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
ddiff --git a/generated/openapi/reviews.json b/generated/openapi/reviews.json
iindex 58bd53d2..b7c46d1b 100644
---- a/generated/openapi/reviews.json
++++ b/generated/openapi/reviews.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Reviews API",
-- "version": "0.256.0",
-- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
ddiff --git a/generated/openapi/run.json b/generated/openapi/run.json
iindex baee3bc2..d4c5f572 100644
---- a/generated/openapi/run.json
++++ b/generated/openapi/run.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Run API",
-- "version": "0.256.0",
-- "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
ddiff --git a/generated/openapi/search.json b/generated/openapi/search.json
iindex f35f8ccc..689aeff9 100644
---- a/generated/openapi/search.json
++++ b/generated/openapi/search.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Search API",
-- "version": "0.256.0",
-- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
ddiff --git a/generated/openapi/services.json b/generated/openapi/services.json
iindex ce2a6990..3b31c30b 100644
---- a/generated/openapi/services.json
++++ b/generated/openapi/services.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Services API",
-- "version": "0.256.0",
-- "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
ddiff --git a/generated/openapi/sources.json b/generated/openapi/sources.json
iindex 319cba3c..a276c38b 100644
---- a/generated/openapi/sources.json
++++ b/generated/openapi/sources.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Sources API",
-- "version": "0.256.0",
-- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
ddiff --git a/generated/openapi/space.json b/generated/openapi/space.json
iindex 13230c0c..719cc697 100644
---- a/generated/openapi/space.json
++++ b/generated/openapi/space.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Space API",
-- "version": "0.256.0",
-- "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
ddiff --git a/generated/openapi/tips.json b/generated/openapi/tips.json
iindex 5d957327..4616ccc0 100644
---- a/generated/openapi/tips.json
++++ b/generated/openapi/tips.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Tips API",
-- "version": "0.256.0",
-- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
ddiff --git a/generated/openapi/tools.json b/generated/openapi/tools.json
iindex bfac4466..8fb822e4 100644
---- a/generated/openapi/tools.json
++++ b/generated/openapi/tools.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Tools API",
-- "version": "0.256.0",
-- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
ddiff --git a/generated/openapi/trade.json b/generated/openapi/trade.json
iindex 09cd63dd..43d81ca1 100644
---- a/generated/openapi/trade.json
++++ b/generated/openapi/trade.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Trade API",
-- "version": "0.256.0",
-- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
ddiff --git a/generated/openapi/vip.json b/generated/openapi/vip.json
iindex a4cbc392..c34188c3 100644
---- a/generated/openapi/vip.json
++++ b/generated/openapi/vip.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.VIP API",
-- "version": "0.256.0",
-- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
ddiff --git a/generated/openapi/vision.json b/generated/openapi/vision.json
iindex 96f30578..bf45596f 100644
---- a/generated/openapi/vision.json
++++ b/generated/openapi/vision.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Vision API",
-- "version": "0.256.0",
-- "description": "Routes of OpenVibe.Vision as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenVibe.Vision as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
ddiff --git a/generated/openapi/watch.json b/generated/openapi/watch.json
iindex 0cc75dd8..7c833c00 100644
---- a/generated/openapi/watch.json
++++ b/generated/openapi/watch.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Watch API",
-- "version": "0.256.0",
-- "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
ddiff --git a/generated/openapi/wiki.json b/generated/openapi/wiki.json
iindex 52db0b88..c739355a 100644
---- a/generated/openapi/wiki.json
++++ b/generated/openapi/wiki.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Wiki API",
-- "version": "0.256.0",
-- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
ddiff --git a/generated/openapi/work.json b/generated/openapi/work.json
iindex 25e0b895..5faa7c7f 100644
---- a/generated/openapi/work.json
++++ b/generated/openapi/work.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Work API",
-- "version": "0.256.0",
-- "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
ddiff --git a/generated/openapi/zone.json b/generated/openapi/zone.json
iindex 9a43d790..eb36fd51 100644
---- a/generated/openapi/zone.json
++++ b/generated/openapi/zone.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Zone API",
-- "version": "0.256.0",
-- "description": "Routes of OpenVibe.Zone as the OpenVibe contracts describe them (openvibe-contracts 0.256.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.257.0",
++ "description": "Routes of OpenVibe.Zone as the OpenVibe contracts describe them (openvibe-contracts 0.257.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"
ddiff --git a/generated/typescript/index.d.ts b/generated/typescript/index.d.ts
iindex 3bc83834..f4d33819 100644
---- a/generated/typescript/index.d.ts
++++ b/generated/typescript/index.d.ts
@@@ -280293,3 +280293,188 @@ export interface AiMemoryWriteRequest {
status?: "active" | "suggested" | "archived" | "conflict";
source?: string;
}
++
++/** [email protected] (owner: ai) */
++/**
++ * ai.me-credential@1 (OpenVibe.AI; L36 Y1): one of the person's own provider keys as the person reads it from GET /api/v1/me/credentials. It is never the key: `hint` is the last four characters, and `last_error` is a redacted line that never carries the key. `status` is what the last save-time or run-time check found: ok, failing or untested. `models_count` is how many models the provider listed when it was last tested. Own keys are free to their owner: no fee, no cap and no network budget.
++ */
++export interface AiMeCredentialV1 {
++ provider:
++ | "anthropic"
++ | "openai"
++ | "gemini"
++ | "deepseek"
++ | "mistral"
++ | "xai"
++ | "groq"
++ | "cerebras"
++ | "together"
++ | "fireworks"
++ | "openrouter"
++ | "minimax"
++ | "custom";
++ /**
++ * The key's last four characters, so the person can tell two keys apart. Never more.
++ */
++ hint: string;
++ /**
++ * The endpoint the person chose, or null for the provider's own (taken from the console presets).
++ */
++ base_url: string | null;
++ /**
++ * The person's own daily cap on this key; null or 0 means no cap. The network's budgets never apply to their own key.
++ */
++ budget_usd_per_day: number | null;
++ status: "ok" | "failing" | "untested";
++ checked_at: string | null;
++ /**
++ * Why the key last failed, with the key and any other secret redacted. Never the key itself.
++ */
++ last_error: string | null;
++ models_count: number;
++ created_at: string;
++ updated_at?: string;
++}
++
++/** [email protected] (owner: ai) */
++/**
++ * ai.me-credential-put@1 (OpenVibe.AI; L36 Y1): the person stores or replaces their own provider key at PUT /api/v1/me/credentials/{provider}. Only the person's own Network token or their AI site session may send it. `key` is required the first time and whenever `base_url` changes; a later save may leave it out to keep the stored key. The key is sealed at rest, tested on save and never returned, logged or put in an error.
++ */
++export type AiMeCredentialPutV1 = {
++ [k: string]: unknown | undefined;
++} & {
++ /**
++ * The provider. The route names it too; when both are present they must agree. A custom provider must name its endpoint in base_url.
++ */
++ provider:
++ | "anthropic"
++ | "openai"
++ | "gemini"
++ | "deepseek"
++ | "mistral"
++ | "xai"
++ | "groq"
++ | "cerebras"
++ | "together"
++ | "fireworks"
++ | "openrouter"
++ | "minimax"
++ | "custom";
++ /**
++ * The provider key. Required the first time and whenever base_url changes; optional afterwards, when the stored key is kept. Never returned.
++ */
++ key?: string;
++ /**
++ * The endpoint, for a custom provider or to override the provider's own. https only, no credentials, query or fragment; reached through the guarded fetcher's egress rules.
++ */
++ base_url?: string;
++ /**
++ * The person's own daily cap on this key; 0 or absent means no cap.
++ */
++ budget_usd_per_day?: number;
++ /**
++ * The model this key should use per kind of work. `auto` is not a model: absent means the provider's default, and the person's ai.me-preferences choose between keys.
++ */
++ models?: {
++ chat?: string;
++ code?: string;
++ vision?: string;
++ image?: string;
++ };
++};
++
++/** [email protected] (owner: ai) */
++/**
++ * ai.me-credential-test@1 (OpenVibe.AI; L36 Y1/Y8): the answer to POST /api/v1/me/credentials/{provider}/test and to the test that runs on save. It lists the provider's models (up to five samples), or, when it lists none, makes a one-token request to its cheapest model. `ok` is false for a refused key, an empty endpoint or a timeout (10 s). `error` is a redacted line that never carries the key. `balance` is present only when the provider reports one.
++ */
++export interface AiMeCredentialTestV1 {
++ ok: boolean;
++ models_count: number;
++ /**
++ * @maxItems 5
++ */
++ sample_models:
++ | []
++ | [string]
++ | [string, string]
++ | [string, string, string]
++ | [string, string, string, string]
++ | [string, string, string, string, string];
++ latency_ms: number;
++ balance: null | {
++ amount: number;
++ currency: string;
++ };
++ error: null | {
++ code: string;
++ message: string;
++ };
++}
++
++/** [email protected] (owner: ai) */
++/**
++ * ai.me-credential-list@1 (OpenVibe.AI; L36 Y1): GET /api/v1/me/credentials — every provider key the person holds themselves, without any key. Ordered by provider so a page can render it steadily.
++ */
++export interface AiMeCredentialListV1 {
++ /**
++ * @maxItems 32
++ */
++ credentials: AiMeCredentialV1[];
++}
++
++/** [email protected] (owner: ai) */
++/**
++ * ai.me-preferences@1 (OpenVibe.AI; L36 Y1/Y3): how one person's AI runs route, stored per person under GET/PUT /api/v1/me/ai/preferences. `only_my_keys` means exactly that: a failed or missing key is never quietly replaced by a paid route. `free_first` puts free tiers ahead of paid ones. `price_ceiling_usd_per_mtok` caps the network's paid fallback per million tokens; the person's own keys are never capped. Each default is a model id or `auto`.
++ */
++export interface AiMePreferencesV1 {
++ only_my_keys: boolean;
++ free_first: boolean;
++ price_ceiling_usd_per_mtok: number | null;
++ defaults: {
++ /**
++ * A model id, or `auto` to let placement choose.
++ */
++ text: string;
++ /**
++ * A model id, or `auto` to let placement choose.
++ */
++ code: string;
++ /**
++ * A model id, or `auto` to let placement choose.
++ */
++ vision: string;
++ /**
++ * A model id, or `auto` to let placement choose.
++ */
++ image: string;
++ };
++ updated_at?: string | null;
++}
++
++/** [email protected] (owner: ai) */
++/**
++ * ai.credential.failed v1 (OpenVibe.AI; L36 Y8): a provider refused one of a person's own keys with 401 or 403 during a run. The key's status becomes failing and this event tells the products that may show it. `error` is a redacted line that never carries the key. Envelope: on_behalf_of the person, subject { type: credential, id: <subject>:<provider> }, visibility internal, priority important, actor service:ai.
++ */
++export interface AiCredentialFailedPayload {
++ subject: string;
++ provider:
++ | "anthropic"
++ | "openai"
++ | "gemini"
++ | "deepseek"
++ | "mistral"
++ | "xai"
++ | "groq"
++ | "cerebras"
++ | "together"
++ | "fireworks"
++ | "openrouter"
++ | "minimax"
++ | "custom";
++ status: "failing";
++ checked_at: string;
++ /**
++ * The redacted reason. Never the key.
++ */
++ error: string;
++}
ddiff --git a/manifests/capabilities/ai.me.manage.json b/manifests/capabilities/ai.me.manage.json
nnew file mode 100644
iindex 00000000..dc5b6c5e
---- /dev/null
++++ b/manifests/capabilities/ai.me.manage.json
@@@ -0,0 +1,26 @@
++{
++ "id": "ai.me.manage",
++ "version": "1.0",
++ "owner": "ai",
++ "status": "active",
++ "visibility": "public",
++ "description": "The person stores, tests and deletes their own provider keys and chooses how their runs route (L36 Y1/Y3): PUT/DELETE /api/v1/me/credentials/{provider}, POST /api/v1/me/credentials/{provider}/test and PUT /api/v1/me/ai/preferences. A key is tested on save (models, or a one-token request), sealed at rest and never returned, logged or put in an error. Only the person may call it — a service token or any token carrying on_behalf_of is refused, whatever capabilities it holds.",
++ "permissions": [
++ "ai:me:write"
++ ],
++ "resourceConstraints": [
++ "owner"
++ ],
++ "quotaClass": "ai-credentials",
++ "events": [
++ "ai.credential.failed"
++ ],
++ "implementedBy": [
++ "PUT /api/v1/me/credentials/:provider",
++ "DELETE /api/v1/me/credentials/:provider",
++ "POST /api/v1/me/credentials/:provider/test",
++ "PUT /api/v1/me/ai/preferences"
++ ],
++ "inputSchema": "ai.me-credential-put@1",
++ "outputSchema": "ai.me-credential@1"
++}
ddiff --git a/manifests/capabilities/ai.me.read.json b/manifests/capabilities/ai.me.read.json
nnew file mode 100644
iindex 00000000..18762662
---- /dev/null
++++ b/manifests/capabilities/ai.me.read.json
@@@ -0,0 +1,22 @@
++{
++ "id": "ai.me.read",
++ "version": "1.0",
++ "owner": "ai",
++ "status": "active",
++ "visibility": "public",
++ "description": "The person's own AI settings (L36 Y1): GET /api/v1/me/credentials lists the provider keys they hold themselves, never a key (a four-character hint only), and GET /api/v1/me/ai/preferences reads how their runs route. Only the person may call it — their Network user token, or the person session on AI's own site behind a same-origin check. A service token is refused, and so is any token carrying on_behalf_of, whatever capabilities it holds.",
++ "permissions": [
++ "ai:me:read"
++ ],
++ "resourceConstraints": [
++ "owner"
++ ],
++ "quotaClass": "ai-credentials",
++ "events": [],
++ "implementedBy": [
++ "GET /api/v1/me/credentials",
++ "GET /api/v1/me/ai/preferences"
++ ],
++ "inputSchema": "common.no-body@1",
++ "outputSchema": "ai.me-credential-list@1"
++}
ddiff --git a/manifests/services/ai.json b/manifests/services/ai.json
iindex c21c0681..beba4240 100644
---- a/manifests/services/ai.json
++++ b/manifests/services/ai.json
@@@ -30,13 +30,16 @@
"ai.workflow.manage",
"ai.actions.read",
"ai.memory.read",
-- "ai.memory.write"
++ "ai.memory.write",
++ "ai.me.read",
++ "ai.me.manage"
],
"eventsProduced": [
"ai.run.queued",
"ai.run.succeeded",
"ai.run.failed",
-- "ai.run.cached"
++ "ai.run.cached",
++ "ai.credential.failed"
],
"eventsConsumed": [],
"namespacesOwned": [
ddiff --git a/package.json b/package.json
iindex 1cb44079..3695f463 100644
---- a/package.json
++++ b/package.json
@@@ -1,6 +1,6 @@
{
"name": "openvibe-contracts",
-- "version": "0.256.0",
++ "version": "0.257.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",