openvibers/openvibe.contracts Commits
Contracts 0.116.0: OpenVibe.Food, OpenVibe.Help and OpenVibe.Work become services (plan T19) (#63)
Changes
@@@ -4,6 +4,21 @@ All notable changes to `openvibe-contracts`. Releases are git tags (`vX.Y.Z`) thfrom `https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/<tag>`. Before v0.30.0,the notes were in the tag and commit messages (`git tag -n1`).++## 0.116.0 — 2026-10-08++++**OpenVibe.Food, OpenVibe.Help and OpenVibe.Work become services** (plan T19 "flesh the network out"; owner 2026-10-08).++- `manifests/services/food.json` (port 4970, `openvibe.food` live, site position 24): food banks and budget grocers++ near you from OpenStreetMap (ODbL attribution on every answer), the budget food list, meal plans and a pantry.++- `manifests/services/help.json` (port 5020, `openvibe.help` live, position 25): the network's help centre built from++ this repository's catalog, and support tickets. No capabilities: a ticket is a person's own.++- `manifests/services/work.json` (port 4960, `openvibe.work` live, position 26): job listings from open job boards with++ their provenance (excerpt only, original linked), search and saved searches. No capabilities yet.++- Capabilities (audience `openvibe.food`): `food.plan.write` (sensitive), `food.plan.read`, `food.pantry.write`++ (sensitive), `food.pantry.read`; the place search and the food list are public reads with no capability.++- Schemas: `food.plan-create-request@1`, `food.plan@1`, `food.plan-result@1`, `food.pantry-request@1`,++ `food.pantry-result@1`.++- The three product manifests point at their services.++## 0.115.0 — 2026-10-08**OpenVibe.Actor becomes a service** (plan T17, ADR-044; owner direction 2026-10-08: Actor is OpenVibe's own general agent@@@ -6353,6 +6353,51 @@"compatibility": "backward","status": "active","adr": "ADR-044"++ },++ {++ "id": "food.pantry-request",++ "version": "1.0.0",++ "owner": "food",++ "schema": "food/pantry-request.v1.json",++ "visibility": "public",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "food.pantry-result",++ "version": "1.0.0",++ "owner": "food",++ "schema": "food/pantry-result.v1.json",++ "visibility": "public",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "food.plan",++ "version": "1.0.0",++ "owner": "food",++ "schema": "food/plan.v1.json",++ "visibility": "public",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "food.plan-create-request",++ "version": "1.0.0",++ "owner": "food",++ "schema": "food/plan-create-request.v1.json",++ "visibility": "public",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "food.plan-result",++ "version": "1.0.0",++ "owner": "food",++ "schema": "food/plan-result.v1.json",++ "visibility": "public",++ "compatibility": "backward",++ "status": "active"}]}nnew file mode 100644@@@ -0,0 +1,22 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/food/pantry-request.v1.json",++ "title": "FoodPantryRequest",++ "description": "food.pantry-request@1: PUT /api/v1/pantry on OpenVibe.Food replaces the pantry with exactly these food slugs.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "items"++ ],++ "properties": {++ "items": {++ "type": "array",++ "maxItems": 200,++ "items": {++ "type": "string",++ "minLength": 1,++ "maxLength": 80++ }++ }++ }++}nnew file mode 100644@@@ -0,0 +1,110 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/food/pantry-result.v1.json",++ "title": "FoodPantryResult",++ "description": "food.pantry-result@1: GET and PUT /api/v1/pantry on OpenVibe.Food: the slugs, each food as the food list shows it, and the suggestions matched against them (can_make: everything there; almost: one or two items missing, named).",++ "type": "object",++ "required": [++ "items",++ "foods",++ "suggestions"++ ],++ "properties": {++ "items": {++ "type": "array",++ "items": {++ "type": "string"++ }++ },++ "foods": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "slug",++ "name"++ ],++ "properties": {++ "slug": {++ "type": "string"++ },++ "name": {++ "type": "string"++ }++ }++ }++ },++ "suggestions": {++ "type": "object",++ "required": [++ "can_make",++ "almost"++ ],++ "properties": {++ "can_make": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "slug",++ "name"++ ],++ "properties": {++ "slug": {++ "type": "string"++ },++ "name": {++ "type": "string"++ },++ "serves": {++ "type": "integer"++ },++ "minutes": {++ "type": "integer"++ },++ "missing": {++ "type": "array",++ "items": {++ "type": "string"++ }++ }++ }++ }++ },++ "almost": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "slug",++ "name"++ ],++ "properties": {++ "slug": {++ "type": "string"++ },++ "name": {++ "type": "string"++ },++ "serves": {++ "type": "integer"++ },++ "minutes": {++ "type": "integer"++ },++ "missing": {++ "type": "array",++ "items": {++ "type": "string"++ }++ }++ }++ }++ }++ }++ },++ "note": {++ "type": "string"++ }++ }++}nnew file mode 100644@@@ -0,0 +1,39 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/food/plan-create-request.v1.json",++ "title": "FoodPlanCreateRequest",++ "description": "food.plan-create-request@1: POST /api/v1/plans on OpenVibe.Food. people 1-12, days 1-14, budget (US dollars for the whole plan) optional, title optional.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "people",++ "days"++ ],++ "properties": {++ "people": {++ "type": "integer",++ "minimum": 1,++ "maximum": 12++ },++ "days": {++ "type": "integer",++ "minimum": 1,++ "maximum": 14++ },++ "budget": {++ "type": [++ "number",++ "null"++ ],++ "minimum": 1,++ "maximum": 5000++ },++ "title": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 120++ }++ }++}nnew file mode 100644@@@ -0,0 +1,26 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/food/plan-result.v1.json",++ "title": "FoodPlanResult",++ "description": "food.plan-result@1: answers of the plan routes on OpenVibe.Food: GET /api/v1/plans → { plans }; GET /api/v1/plans/:id and POST /api/v1/plans (201) → the plan itself (food.plan@1).",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "plans"++ ],++ "additionalProperties": false,++ "properties": {++ "plans": {++ "type": "array",++ "items": {++ "$ref": "plan.v1.json"++ }++ }++ }++ },++ {++ "$ref": "plan.v1.json"++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,115 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/food/plan.v1.json",++ "title": "FoodPlan",++ "description": "food.plan@1: one saved plan on OpenVibe.Food.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "id",++ "title",++ "people",++ "days",++ "budget",++ "created_at",++ "plan"++ ],++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^pln_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "title": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 120++ },++ "people": {++ "type": "integer",++ "minimum": 1++ },++ "days": {++ "type": "integer",++ "minimum": 1++ },++ "budget": {++ "type": [++ "number",++ "null"++ ],++ "minimum": 0++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "plan": {++ "type": "object",++ "description": "The plan as Food worked it out: spec, items (the shopping list), by_store, menu (one entry a day: breakfast, lunch, dinner, snack), group_coverage and totals (cost, cost_per_person_day, kcal, protein). Food may add fields.",++ "required": [++ "spec",++ "items",++ "menu",++ "totals"++ ],++ "properties": {++ "spec": {++ "type": "object",++ "required": [++ "people",++ "days"++ ],++ "properties": {++ "people": {++ "type": "integer"++ },++ "days": {++ "type": "integer"++ },++ "budget": {++ "type": [++ "number",++ "null"++ ]++ }++ }++ },++ "items": {++ "type": "array",++ "items": {++ "type": "object"++ }++ },++ "menu": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "day"++ ],++ "properties": {++ "day": {++ "type": "integer",++ "minimum": 1++ }++ }++ }++ },++ "totals": {++ "type": "object",++ "required": [++ "cost"++ ],++ "properties": {++ "cost": {++ "type": "number",++ "minimum": 0++ }++ }++ }++ }++ }++ }++}nnew file mode 100644@@@ -0,0 +1,5 @@++{++ "items": [++ ""++ ]++}nnew file mode 100644@@@ -0,0 +1,3 @@++{++ "items": "eggs"++}nnew file mode 100644@@@ -0,0 +1,3 @@++{++ "items": []++}nnew file mode 100644@@@ -0,0 +1,6 @@++{++ "items": [++ "eggs-dozen",++ "rice-long-grain-5lb"++ ]++}nnew file mode 100644@@@ -0,0 +1,4 @@++{++ "items": [],++ "foods": []++}nnew file mode 100644@@@ -0,0 +1,26 @@++{++ "items": [++ "eggs-dozen"++ ],++ "foods": [++ {++ "slug": "eggs-dozen",++ "name": "Eggs, dozen"++ }++ ],++ "suggestions": {++ "can_make": [],++ "almost": [++ {++ "slug": "egg-fried-rice",++ "name": "Egg fried rice",++ "serves": 2,++ "minutes": 15,++ "missing": [++ "Rice"++ ]++ }++ ]++ },++ "note": "Basic suggestions."++}nnew file mode 100644@@@ -0,0 +1,5 @@++{++ "people": 2,++ "days": 2,++ "owner": "someone"++}nnew file mode 100644@@@ -0,0 +1,3 @@++{++ "people": 2++}nnew file mode 100644@@@ -0,0 +1,4 @@++{++ "people": 40,++ "days": 3++}nnew file mode 100644@@@ -0,0 +1,6 @@++{++ "people": 4,++ "days": 7,++ "budget": 120,++ "title": "Family week"++}nnew file mode 100644@@@ -0,0 +1,4 @@++{++ "people": 1,++ "days": 3++}nnew file mode 100644@@@ -0,0 +1,41 @@++{++ "plans": {++ "id": "pln_01JZ6Q4X8M2N3P4R5S6T7V8W9X",++ "title": "Week for two",++ "people": 2,++ "days": 7,++ "budget": 80,++ "created_at": "2026-10-08T20:00:00.000Z",++ "plan": {++ "spec": {++ "people": 2,++ "days": 7,++ "budget": 80++ },++ "items": [++ {++ "slug": "rice-long-grain-5lb",++ "name": "Long grain rice, 5 lb",++ "packs": 1++ }++ ],++ "menu": [++ {++ "day": 1,++ "breakfast": [++ "Oats"++ ],++ "lunch": [],++ "dinner": [++ "Rice"++ ],++ "snack": []++ }++ ],++ "totals": {++ "cost": 41.2,++ "kcal": 28000++ }++ }++ }++}nnew file mode 100644@@@ -0,0 +1,3 @@++{++ "plans": []++}nnew file mode 100644@@@ -0,0 +1,43 @@++{++ "plans": [++ {++ "id": "pln_01JZ6Q4X8M2N3P4R5S6T7V8W9X",++ "title": "Week for two",++ "people": 2,++ "days": 7,++ "budget": 80,++ "created_at": "2026-10-08T20:00:00.000Z",++ "plan": {++ "spec": {++ "people": 2,++ "days": 7,++ "budget": 80++ },++ "items": [++ {++ "slug": "rice-long-grain-5lb",++ "name": "Long grain rice, 5 lb",++ "packs": 1++ }++ ],++ "menu": [++ {++ "day": 1,++ "breakfast": [++ "Oats"++ ],++ "lunch": [],++ "dinner": [++ "Rice"++ ],++ "snack": []++ }++ ],++ "totals": {++ "cost": 41.2,++ "kcal": 28000++ }++ }++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,39 @@++{++ "id": "pln_01JZ6Q4X8M2N3P4R5S6T7V8W9X",++ "title": "Week for two",++ "people": 2,++ "days": 7,++ "budget": 80,++ "created_at": "2026-10-08T20:00:00.000Z",++ "plan": {++ "spec": {++ "people": 2,++ "days": 7,++ "budget": 80++ },++ "items": [++ {++ "slug": "rice-long-grain-5lb",++ "name": "Long grain rice, 5 lb",++ "packs": 1++ }++ ],++ "menu": [++ {++ "day": 1,++ "breakfast": [++ "Oats"++ ],++ "lunch": [],++ "dinner": [++ "Rice"++ ],++ "snack": []++ }++ ],++ "totals": {++ "cost": 41.2,++ "kcal": 28000++ }++ }++}nnew file mode 100644@@@ -0,0 +1,39 @@++{++ "id": "plan-1",++ "title": "Week for two",++ "people": 2,++ "days": 7,++ "budget": 80,++ "created_at": "2026-10-08T20:00:00.000Z",++ "plan": {++ "spec": {++ "people": 2,++ "days": 7,++ "budget": 80++ },++ "items": [++ {++ "slug": "rice-long-grain-5lb",++ "name": "Long grain rice, 5 lb",++ "packs": 1++ }++ ],++ "menu": [++ {++ "day": 1,++ "breakfast": [++ "Oats"++ ],++ "lunch": [],++ "dinner": [++ "Rice"++ ],++ "snack": []++ }++ ],++ "totals": {++ "cost": 41.2,++ "kcal": 28000++ }++ }++}nnew file mode 100644@@@ -0,0 +1,8 @@++{++ "id": "pln_01JZ6Q4X8M2N3P4R5S6T7V8W9X",++ "title": "Week for two",++ "people": 2,++ "days": 7,++ "budget": 80,++ "created_at": "2026-10-08T20:00:00.000Z"++}nnew file mode 100644@@@ -0,0 +1,39 @@++{++ "id": "pln_01JZ6Q4X8M2N3P4R5S6T7V8W9X",++ "title": "Week for two",++ "people": 2,++ "days": 7,++ "budget": 80,++ "created_at": "2026-10-08T20:00:00.000Z",++ "plan": {++ "spec": {++ "people": 2,++ "days": 7,++ "budget": 80++ },++ "items": [++ {++ "slug": "rice-long-grain-5lb",++ "name": "Long grain rice, 5 lb",++ "packs": 1++ }++ ],++ "menu": [++ {++ "day": 1,++ "breakfast": [++ "Oats"++ ],++ "lunch": [],++ "dinner": [++ "Rice"++ ],++ "snack": []++ }++ ],++ "totals": {++ "cost": 41.2,++ "kcal": 28000++ }++ }++}@@@ -73477,6 +73477,343 @@}}}++ },++ "food.pantry-request": {++ "version": "1.0.0",++ "owner": "food",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/food/pantry-request.v1.json",++ "title": "FoodPantryRequest",++ "description": "food.pantry-request@1: PUT /api/v1/pantry on OpenVibe.Food replaces the pantry with exactly these food slugs.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "items"++ ],++ "properties": {++ "items": {++ "type": "array",++ "maxItems": 200,++ "items": {++ "type": "string",++ "minLength": 1,++ "maxLength": 80++ }++ }++ }++ }++ },++ "food.pantry-result": {++ "version": "1.0.0",++ "owner": "food",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/food/pantry-result.v1.json",++ "title": "FoodPantryResult",++ "description": "food.pantry-result@1: GET and PUT /api/v1/pantry on OpenVibe.Food: the slugs, each food as the food list shows it, and the suggestions matched against them (can_make: everything there; almost: one or two items missing, named).",++ "type": "object",++ "required": [++ "items",++ "foods",++ "suggestions"++ ],++ "properties": {++ "items": {++ "type": "array",++ "items": {++ "type": "string"++ }++ },++ "foods": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "slug",++ "name"++ ],++ "properties": {++ "slug": {++ "type": "string"++ },++ "name": {++ "type": "string"++ }++ }++ }++ },++ "suggestions": {++ "type": "object",++ "required": [++ "can_make",++ "almost"++ ],++ "properties": {++ "can_make": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "slug",++ "name"++ ],++ "properties": {++ "slug": {++ "type": "string"++ },++ "name": {++ "type": "string"++ },++ "serves": {++ "type": "integer"++ },++ "minutes": {++ "type": "integer"++ },++ "missing": {++ "type": "array",++ "items": {++ "type": "string"++ }++ }++ }++ }++ },++ "almost": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "slug",++ "name"++ ],++ "properties": {++ "slug": {++ "type": "string"++ },++ "name": {++ "type": "string"++ },++ "serves": {++ "type": "integer"++ },++ "minutes": {++ "type": "integer"++ },++ "missing": {++ "type": "array",++ "items": {++ "type": "string"++ }++ }++ }++ }++ }++ }++ },++ "note": {++ "type": "string"++ }++ }++ }++ },++ "food.plan": {++ "version": "1.0.0",++ "owner": "food",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/food/plan.v1.json",++ "title": "FoodPlan",++ "description": "food.plan@1: one saved plan on OpenVibe.Food.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "id",++ "title",++ "people",++ "days",++ "budget",++ "created_at",++ "plan"++ ],++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^pln_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "title": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 120++ },++ "people": {++ "type": "integer",++ "minimum": 1++ },++ "days": {++ "type": "integer",++ "minimum": 1++ },++ "budget": {++ "type": [++ "number",++ "null"++ ],++ "minimum": 0++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "plan": {++ "type": "object",++ "description": "The plan as Food worked it out: spec, items (the shopping list), by_store, menu (one entry a day: breakfast, lunch, dinner, snack), group_coverage and totals (cost, cost_per_person_day, kcal, protein). Food may add fields.",++ "required": [++ "spec",++ "items",++ "menu",++ "totals"++ ],++ "properties": {++ "spec": {++ "type": "object",++ "required": [++ "people",++ "days"++ ],++ "properties": {++ "people": {++ "type": "integer"++ },++ "days": {++ "type": "integer"++ },++ "budget": {++ "type": [++ "number",++ "null"++ ]++ }++ }++ },++ "items": {++ "type": "array",++ "items": {++ "type": "object"++ }++ },++ "menu": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "day"++ ],++ "properties": {++ "day": {++ "type": "integer",++ "minimum": 1++ }++ }++ }++ },++ "totals": {++ "type": "object",++ "required": [++ "cost"++ ],++ "properties": {++ "cost": {++ "type": "number",++ "minimum": 0++ }++ }++ }++ }++ }++ }++ }++ },++ "food.plan-create-request": {++ "version": "1.0.0",++ "owner": "food",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/food/plan-create-request.v1.json",++ "title": "FoodPlanCreateRequest",++ "description": "food.plan-create-request@1: POST /api/v1/plans on OpenVibe.Food. people 1-12, days 1-14, budget (US dollars for the whole plan) optional, title optional.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "people",++ "days"++ ],++ "properties": {++ "people": {++ "type": "integer",++ "minimum": 1,++ "maximum": 12++ },++ "days": {++ "type": "integer",++ "minimum": 1,++ "maximum": 14++ },++ "budget": {++ "type": [++ "number",++ "null"++ ],++ "minimum": 1,++ "maximum": 5000++ },++ "title": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 120++ }++ }++ }++ },++ "food.plan-result": {++ "version": "1.0.0",++ "owner": "food",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/food/plan-result.v1.json",++ "title": "FoodPlanResult",++ "description": "food.plan-result@1: answers of the plan routes on OpenVibe.Food: GET /api/v1/plans → { plans }; GET /api/v1/plans/:id and POST /api/v1/plans (201) → the plan itself (food.plan@1).",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "plans"++ ],++ "additionalProperties": false,++ "properties": {++ "plans": {++ "type": "array",++ "items": {++ "$ref": "plan.v1.json"++ }++ }++ }++ },++ {++ "$ref": "plan.v1.json"++ }++ ]++ }}}}@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Actor API",-- "version": "0.115.0",-- "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",++ "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.AI API",-- "version": "0.115.0",-- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",++ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Billing API",-- "version": "0.115.0",-- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",++ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Blog API",-- "version": "0.115.0",-- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",++ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Bot API",-- "version": "0.115.0",-- "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",++ "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Chat API",-- "version": "0.115.0",-- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",++ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Community API",-- "version": "0.115.0",-- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",++ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Coupons API",-- "version": "0.115.0",-- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",++ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Deals API",-- "version": "0.115.0",-- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",++ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Events API",-- "version": "0.115.0",-- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",++ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"nnew file mode 100644@@@ -0,0 +1,656 @@++{++ "openapi": "3.1.0",++ "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",++ "info": {++ "title": "OpenVibe.Food API",++ "version": "0.116.0",++ "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "license": {++ "name": "MIT",++ "identifier": "MIT"++ }++ },++ "servers": [++ {++ "url": "https://openvibe.food"++ }++ ],++ "paths": {++ "/api/v1/pantry": {++ "get": {++ "operationId": "get_api_v1_pantry",++ "summary": "Read a caller's pantry with the suggestions matched against it: GET /api/v1/pantry → { items, foods, suggestions: { can_make, almost }, note }.",++ "description": "**food.pantry.read** (public): Read a caller's pantry with the suggestions matched against it: GET /api/v1/pantry → { items, foods, suggestions: { can_make, almost }, note }. The suggestions are deterministic matches against basic recipes, not dietary advice.",++ "tags": [++ "food.pantry"++ ],++ "x-openvibe-capabilities": [++ "food.pantry.read"++ ],++ "x-openvibe-visibility": [++ "public"++ ],++ "x-openvibe-quota-class": "food-read",++ "security": [++ {++ "openvibeToken": [++ "food.pantry.read"++ ]++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/food.pantry-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ },++ "put": {++ "operationId": "put_api_v1_pantry",++ "summary": "Replace a caller's pantry with exactly this list: PUT /api/v1/pantry with { items: [food slug, …] } (at most 200, each one in the food list; GET /api/v1/foods lists them).",++ "description": "**food.pantry.write** (public): Replace a caller's pantry with exactly this list: PUT /api/v1/pantry with { items: [food slug, …] } (at most 200, each one in the food list; GET /api/v1/foods lists them). Answers like a read.",++ "tags": [++ "food.pantry"++ ],++ "x-openvibe-capabilities": [++ "food.pantry.write"++ ],++ "x-openvibe-visibility": [++ "public"++ ],++ "x-openvibe-quota-class": "food-write",++ "security": [++ {++ "openvibeToken": [++ "food.pantry.write"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/food.pantry-request.v1"++ }++ }++ }++ },++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/food.pantry-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/api/v1/plans": {++ "get": {++ "operationId": "get_api_v1_plans",++ "summary": "Read the plans a caller saved: GET /api/v1/plans (newest first, ?limit to 50) → { plans }, and GET /api/v1/plans/:id → one plan.",++ "description": "**food.plan.read** (public): Read the plans a caller saved: GET /api/v1/plans (newest first, ?limit to 50) → { plans }, and GET /api/v1/plans/:id → one plan. Only the person who saved a plan (or their tokens) can read it.",++ "tags": [++ "food.plan"++ ],++ "x-openvibe-capabilities": [++ "food.plan.read"++ ],++ "x-openvibe-visibility": [++ "public"++ ],++ "x-openvibe-quota-class": "food-read",++ "security": [++ {++ "openvibeToken": [++ "food.plan.read"++ ]++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/food.plan-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ },++ "post": {++ "operationId": "post_api_v1_plans",++ "summary": "Plan a shop for a household and a budget and save it: POST /api/v1/plans with { people, days, budget?, title?",++ "description": "**food.plan.write** (public): Plan a shop for a household and a budget and save it: POST /api/v1/plans with { people, days, budget?, title? }. Food picks budget items from its food list to cover each food group for every person and day, splits them by the cheapest store and lays out a menu; the saved plan (201, Location /api/v1/plans/:id) is the caller's own.",++ "tags": [++ "food.plan"++ ],++ "x-openvibe-capabilities": [++ "food.plan.write"++ ],++ "x-openvibe-visibility": [++ "public"++ ],++ "x-openvibe-quota-class": "food-write",++ "security": [++ {++ "openvibeToken": [++ "food.plan.write"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/food.plan-create-request.v1"++ }++ }++ }++ },++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/food.plan-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/api/v1/plans/{id}": {++ "get": {++ "operationId": "get_api_v1_plans_id",++ "summary": "Read the plans a caller saved: GET /api/v1/plans (newest first, ?limit to 50) → { plans }, and GET /api/v1/plans/:id → one plan.",++ "description": "**food.plan.read** (public): Read the plans a caller saved: GET /api/v1/plans (newest first, ?limit to 50) → { plans }, and GET /api/v1/plans/:id → one plan. Only the person who saved a plan (or their tokens) can read it.",++ "tags": [++ "food.plan"++ ],++ "x-openvibe-capabilities": [++ "food.plan.read"++ ],++ "x-openvibe-visibility": [++ "public"++ ],++ "x-openvibe-quota-class": "food-read",++ "security": [++ {++ "openvibeToken": [++ "food.plan.read"++ ]++ }++ ],++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/food.plan-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ }++ },++ "components": {++ "schemas": {++ "errors.problem.v1": {++ "title": "Problem",++ "description": "RFC 9457 problem details (application/problem+json) with a stable OpenVibe error code and trace identifiers. The legacy 'error' string is allowed during migration so existing clients keep working.",++ "type": "object",++ "required": [++ "type",++ "title",++ "status",++ "code"++ ],++ "additionalProperties": true,++ "properties": {++ "type": {++ "type": "string",++ "format": "uri-reference"++ },++ "title": {++ "type": "string",++ "minLength": 1++ },++ "status": {++ "type": "integer",++ "minimum": 400,++ "maximum": 599++ },++ "detail": {++ "type": "string"++ },++ "instance": {++ "type": "string",++ "format": "uri-reference"++ },++ "code": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)*$"++ },++ "request_id": {++ "type": "string"++ },++ "trace_id": {++ "type": "string",++ "pattern": "^[0-9a-f]{32}$"++ },++ "errors": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "message"++ ],++ "properties": {++ "path": {++ "type": "string"++ },++ "message": {++ "type": "string"++ }++ }++ }++ },++ "error": {++ "type": "string",++ "description": "Deprecated compatibility field: same text as title/detail for clients that read {error}."++ }++ }++ },++ "food.pantry-request.v1": {++ "title": "FoodPantryRequest",++ "description": "food.pantry-request@1: PUT /api/v1/pantry on OpenVibe.Food replaces the pantry with exactly these food slugs.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "items"++ ],++ "properties": {++ "items": {++ "type": "array",++ "maxItems": 200,++ "items": {++ "type": "string",++ "minLength": 1,++ "maxLength": 80++ }++ }++ }++ },++ "food.pantry-result.v1": {++ "title": "FoodPantryResult",++ "description": "food.pantry-result@1: GET and PUT /api/v1/pantry on OpenVibe.Food: the slugs, each food as the food list shows it, and the suggestions matched against them (can_make: everything there; almost: one or two items missing, named).",++ "type": "object",++ "required": [++ "items",++ "foods",++ "suggestions"++ ],++ "properties": {++ "items": {++ "type": "array",++ "items": {++ "type": "string"++ }++ },++ "foods": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "slug",++ "name"++ ],++ "properties": {++ "slug": {++ "type": "string"++ },++ "name": {++ "type": "string"++ }++ }++ }++ },++ "suggestions": {++ "type": "object",++ "required": [++ "can_make",++ "almost"++ ],++ "properties": {++ "can_make": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "slug",++ "name"++ ],++ "properties": {++ "slug": {++ "type": "string"++ },++ "name": {++ "type": "string"++ },++ "serves": {++ "type": "integer"++ },++ "minutes": {++ "type": "integer"++ },++ "missing": {++ "type": "array",++ "items": {++ "type": "string"++ }++ }++ }++ }++ },++ "almost": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "slug",++ "name"++ ],++ "properties": {++ "slug": {++ "type": "string"++ },++ "name": {++ "type": "string"++ },++ "serves": {++ "type": "integer"++ },++ "minutes": {++ "type": "integer"++ },++ "missing": {++ "type": "array",++ "items": {++ "type": "string"++ }++ }++ }++ }++ }++ }++ },++ "note": {++ "type": "string"++ }++ }++ },++ "food.plan-create-request.v1": {++ "title": "FoodPlanCreateRequest",++ "description": "food.plan-create-request@1: POST /api/v1/plans on OpenVibe.Food. people 1-12, days 1-14, budget (US dollars for the whole plan) optional, title optional.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "people",++ "days"++ ],++ "properties": {++ "people": {++ "type": "integer",++ "minimum": 1,++ "maximum": 12++ },++ "days": {++ "type": "integer",++ "minimum": 1,++ "maximum": 14++ },++ "budget": {++ "type": [++ "number",++ "null"++ ],++ "minimum": 1,++ "maximum": 5000++ },++ "title": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 120++ }++ }++ },++ "food.plan-result.v1": {++ "title": "FoodPlanResult",++ "description": "food.plan-result@1: answers of the plan routes on OpenVibe.Food: GET /api/v1/plans → { plans }; GET /api/v1/plans/:id and POST /api/v1/plans (201) → the plan itself (food.plan@1).",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "plans"++ ],++ "additionalProperties": false,++ "properties": {++ "plans": {++ "type": "array",++ "items": {++ "$ref": "#/components/schemas/food.plan.v1"++ }++ }++ }++ },++ {++ "$ref": "#/components/schemas/food.plan.v1"++ }++ ]++ },++ "food.plan.v1": {++ "title": "FoodPlan",++ "description": "food.plan@1: one saved plan on OpenVibe.Food.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "id",++ "title",++ "people",++ "days",++ "budget",++ "created_at",++ "plan"++ ],++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^pln_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "title": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 120++ },++ "people": {++ "type": "integer",++ "minimum": 1++ },++ "days": {++ "type": "integer",++ "minimum": 1++ },++ "budget": {++ "type": [++ "number",++ "null"++ ],++ "minimum": 0++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "plan": {++ "type": "object",++ "description": "The plan as Food worked it out: spec, items (the shopping list), by_store, menu (one entry a day: breakfast, lunch, dinner, snack), group_coverage and totals (cost, cost_per_person_day, kcal, protein). Food may add fields.",++ "required": [++ "spec",++ "items",++ "menu",++ "totals"++ ],++ "properties": {++ "spec": {++ "type": "object",++ "required": [++ "people",++ "days"++ ],++ "properties": {++ "people": {++ "type": "integer"++ },++ "days": {++ "type": "integer"++ },++ "budget": {++ "type": [++ "number",++ "null"++ ]++ }++ }++ },++ "items": {++ "type": "array",++ "items": {++ "type": "object"++ }++ },++ "menu": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "day"++ ],++ "properties": {++ "day": {++ "type": "integer",++ "minimum": 1++ }++ }++ }++ },++ "totals": {++ "type": "object",++ "required": [++ "cost"++ ],++ "properties": {++ "cost": {++ "type": "number",++ "minimum": 0++ }++ }++ }++ }++ }++ }++ }++ },++ "securitySchemes": {++ "openvibeToken": {++ "type": "http",++ "scheme": "bearer",++ "bearerFormat": "JWT",++ "description": "An OpenVibe Network token for this service: a person's token, an app token (client credentials, audience of the service) or a first-party service token. Its `cap` claim must include the capability the operation names."++ }++ }++ }++}@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Games API",-- "version": "0.115.0",-- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",++ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Host API",-- "version": "0.115.0",-- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",++ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.116.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"@@@ -71,6 +71,13 @@"operations": 15,"capabilities": 8},++ {++ "service": "food",++ "name": "OpenVibe.Food",++ "origin": "https://openvibe.food",++ "operations": 5,++ "capabilities": 4++ },{"service": "games","name": "OpenVibe.Games",@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Live API",-- "version": "0.115.0",-- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",++ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Media API",-- "version": "0.115.0",-- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",++ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Network API",-- "version": "0.115.0",-- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",++ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.News API",-- "version": "0.115.0",-- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",++ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenRe.Stream API",-- "version": "0.115.0",-- "description": "Routes of OpenRe.Stream as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",++ "description": "Routes of OpenRe.Stream as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Reviews API",-- "version": "0.115.0",-- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",++ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Run API",-- "version": "0.115.0",-- "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",++ "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Search API",-- "version": "0.115.0",-- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",++ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Services API",-- "version": "0.115.0",-- "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",++ "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Sources API",-- "version": "0.115.0",-- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",++ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Space API",-- "version": "0.115.0",-- "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",++ "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Tips API",-- "version": "0.115.0",-- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",++ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Tools API",-- "version": "0.115.0",-- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",++ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Trade API",-- "version": "0.115.0",-- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",++ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.VIP API",-- "version": "0.115.0",-- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",++ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Watch API",-- "version": "0.115.0",-- "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",++ "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Wiki API",-- "version": "0.115.0",-- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",++ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.116.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"@@@ -58953,3 +58953,94 @@ export interface ActorAgentListResult {reason?: string;}[];}++++/** [email protected] (owner: food) */++/**++ * food.pantry-request@1: PUT /api/v1/pantry on OpenVibe.Food replaces the pantry with exactly these food slugs.++ */++export interface FoodPantryRequest {++ /**++ * @maxItems 200++ */++ items: string[];++}++++/** [email protected] (owner: food) */++/**++ * food.pantry-result@1: GET and PUT /api/v1/pantry on OpenVibe.Food: the slugs, each food as the food list shows it, and the suggestions matched against them (can_make: everything there; almost: one or two items missing, named).++ */++export interface FoodPantryResult {++ items: string[];++ foods: {++ slug: string;++ name: string;++ }[];++ suggestions: {++ can_make: {++ slug: string;++ name: string;++ serves?: number;++ minutes?: number;++ missing?: string[];++ }[];++ almost: {++ slug: string;++ name: string;++ serves?: number;++ minutes?: number;++ missing?: string[];++ }[];++ };++ note?: string;++}++++/** [email protected] (owner: food) */++/**++ * food.plan@1: one saved plan on OpenVibe.Food.++ */++export interface FoodPlan {++ id: string;++ title: string | null;++ people: number;++ days: number;++ budget: number | null;++ created_at: string;++ /**++ * The plan as Food worked it out: spec, items (the shopping list), by_store, menu (one entry a day: breakfast, lunch, dinner, snack), group_coverage and totals (cost, cost_per_person_day, kcal, protein). Food may add fields.++ */++ plan: {++ spec: {++ people: number;++ days: number;++ budget?: number | null;++ };++ items: {}[];++ menu: {++ day: number;++ }[];++ totals: {++ cost: number;++ };++ };++}++++/** [email protected] (owner: food) */++/**++ * food.plan-create-request@1: POST /api/v1/plans on OpenVibe.Food. people 1-12, days 1-14, budget (US dollars for the whole plan) optional, title optional.++ */++export interface FoodPlanCreateRequest {++ people: number;++ days: number;++ budget?: number | null;++ title?: string | null;++}++++/** [email protected] (owner: food) */++/**++ * food.plan-result@1: answers of the plan routes on OpenVibe.Food: GET /api/v1/plans → { plans }; GET /api/v1/plans/:id and POST /api/v1/plans (201) → the plan itself (food.plan@1).++ */++export type FoodPlanResult =++ | {++ plans: FoodPlan[];++ }++ | FoodPlan;nnew file mode 100644@@@ -0,0 +1,21 @@++{++ "id": "food.pantry.read",++ "version": "1.0",++ "owner": "food",++ "status": "active",++ "visibility": "public",++ "description": "Read a caller's pantry with the suggestions matched against it: GET /api/v1/pantry → { items, foods, suggestions: { can_make, almost }, note }. The suggestions are deterministic matches against basic recipes, not dietary advice.",++ "inputSchema": "common.no-body@1",++ "outputSchema": "food.pantry-result@1",++ "permissions": [++ "food:pantry:read"++ ],++ "resourceConstraints": [++ "owner"++ ],++ "quotaClass": "food-read",++ "events": [],++ "implementedBy": [++ "GET /api/v1/pantry"++ ]++}nnew file mode 100644@@@ -0,0 +1,22 @@++{++ "id": "food.pantry.write",++ "version": "1.0",++ "owner": "food",++ "status": "active",++ "visibility": "public",++ "sensitive": true,++ "description": "Replace a caller's pantry with exactly this list: PUT /api/v1/pantry with { items: [food slug, …] } (at most 200, each one in the food list; GET /api/v1/foods lists them). Answers like a read.",++ "inputSchema": "food.pantry-request@1",++ "outputSchema": "food.pantry-result@1",++ "permissions": [++ "food:pantry:write"++ ],++ "resourceConstraints": [++ "owner"++ ],++ "quotaClass": "food-write",++ "events": [],++ "implementedBy": [++ "PUT /api/v1/pantry"++ ]++}nnew file mode 100644@@@ -0,0 +1,22 @@++{++ "id": "food.plan.read",++ "version": "1.0",++ "owner": "food",++ "status": "active",++ "visibility": "public",++ "description": "Read the plans a caller saved: GET /api/v1/plans (newest first, ?limit to 50) → { plans }, and GET /api/v1/plans/:id → one plan. Only the person who saved a plan (or their tokens) can read it.",++ "inputSchema": "common.no-body@1",++ "outputSchema": "food.plan-result@1",++ "permissions": [++ "food:plans:read"++ ],++ "resourceConstraints": [++ "owner"++ ],++ "quotaClass": "food-read",++ "events": [],++ "implementedBy": [++ "GET /api/v1/plans",++ "GET /api/v1/plans/:id"++ ]++}nnew file mode 100644@@@ -0,0 +1,22 @@++{++ "id": "food.plan.write",++ "version": "1.0",++ "owner": "food",++ "status": "active",++ "visibility": "public",++ "sensitive": true,++ "description": "Plan a shop for a household and a budget and save it: POST /api/v1/plans with { people, days, budget?, title? }. Food picks budget items from its food list to cover each food group for every person and day, splits them by the cheapest store and lays out a menu; the saved plan (201, Location /api/v1/plans/:id) is the caller's own.",++ "inputSchema": "food.plan-create-request@1",++ "outputSchema": "food.plan-result@1",++ "permissions": [++ "food:plans:write"++ ],++ "resourceConstraints": [++ "owner"++ ],++ "quotaClass": "food-write",++ "events": [],++ "implementedBy": [++ "POST /api/v1/plans"++ ]++}@@@ -27,18 +27,6 @@"keywords": "openvibe food, compare food delivery prices, meal kits, meal planning, recipe ideas, grocery tools","launch": "the food tools moving over from OpenVibe.Tools","relationships": {-- "noRepo": true,-- "meanwhile": [-- {-- "label": "Food tools",-- "url": "https://food.openvibe.tools",-- "note": "The food and grocery tools work today."-- },-- {-- "label": "OpenVibe.Coupons",-- "url": "https://openvibe.coupons",-- "note": "Coupons are the sibling room."-- }-- ]++ "service": "food"}}@@@ -27,18 +27,6 @@"keywords": "openvibe help, openvibe support, help center, support tickets, troubleshooting","launch": "the ticket service and the Frame's Help button","relationships": {-- "noRepo": true,-- "meanwhile": [-- {-- "label": "Network status",-- "url": "https://openvibe.network/status",-- "note": "Service health and incidents today."-- },-- {-- "label": "OpenVibe.Wiki",-- "url": "https://openvibe.wiki",-- "note": "Guides and concepts."-- }-- ]++ "service": "help"}}@@@ -27,13 +27,6 @@"keywords": "openvibe work, job search, job listings, job alerts, ai job application assistant","launch": "job listings and alerts","relationships": {-- "noRepo": true,-- "meanwhile": [-- {-- "label": "OpenVibe.Network",-- "url": "https://openvibe.network",-- "note": "One free account for every OpenVibe site, ready today."-- }-- ]++ "service": "work"}}nnew file mode 100644@@@ -0,0 +1,84 @@++{++ "version": "0.1.0",++ "contractRanges": {++ "openvibe-contracts": ">=0.116.0 <1.0.0"++ },++ "id": "food",++ "name": "OpenVibe.Food",++ "status": "alpha",++ "repository": "OpenVibers/OpenVibe.Food",++ "domains": [++ "openvibe.food"++ ],++ "publicOrigin": "https://openvibe.food",++ "exposure": {++ "state": "live",++ "publicSite": "service"++ },++ "capabilities": [++ "food.plan.read",++ "food.plan.write",++ "food.pantry.read",++ "food.pantry.write"++ ],++ "eventsProduced": [],++ "eventsConsumed": [],++ "namespacesOwned": [++ "food.*"++ ],++ "lifecycle": {++ "liveness": {++ "endpoint": "/api/health",++ "means": "the process is up and answers HTTP; it checks nothing else (the database is what ready checks)"++ },++ "shutdown": {++ "signal": "SIGTERM",++ "deadlineSeconds": 10,++ "drains": [++ "HTTP server closed",++ "Network key refresher stopped",++ "store closed"++ ]++ },++ "startupRecovery": {++ "resumes": [++ {++ "kind": "sessions",++ "what": "sign-in sessions are rows in the database, so a person stays signed in across the restart; nothing runs in the background that a restart could leave half done"++ },++ {++ "kind": "state",++ "what": "the OpenStreetMap answer cache is read from food_geo_cache, so a place searched before the stop is not fetched again"++ }++ ]++ },++ "rollback": {++ "conditions": [++ "automatic: ovhost deploy puts the previous sha back and restarts again when the ready URL does not answer 2xx within ready.timeoutSeconds after the restart (exit 3)",++ "manual: ovhost rollback <service> --to <sha>"++ ],++ "window": "automatic only while ovhost waits for readiness after the restart; manual afterwards, as long as no blocker applies",++ "blockers": {++ "none": "migrations 0001-0002 only create Food's own tables; an earlier release has none to read"++ }++ },++ "contracts": {++ "range": ">=0.116.0 <1.0.0"++ },++ "leases": {++ "none": "no claims: one process serves the site"++ }++ },++ "notes": "Port 4970, unit openvibe-food.service. Food near you (food banks and budget grocers from OpenStreetMap through Nominatim and Overpass, one request a second per host, cached 7 days, ODbL attribution on every answer), the budget food list carried over from OpenVibe.Tools with chain-by-chain prices, meal plans for a household and a budget (saved per person), and a pantry with deterministic \"what can I make\" suggestions. Public reads need no token; plans and pantries are a signed-in person's (or an app token's with the capability). Consumes OpenVibe.Network (SSO with PKCE S256 as OAuth client food, JWKS). Owns food_plans, food_pantry and food_geo_cache.",++ "health": "/api/health",++ "ready": "/api/ready",++ "internalOrigin": "http://127.0.0.1:4970",++ "site": {++ "name": "Food",++ "icon": "food",++ "tagline": "What should we eat?",++ "what": "Find food banks and budget grocers near you, plan a week of meals for your household and budget, and see what your cupboard can make.",++ "legalProfile": "tools",++ "position": 24++ }++}nnew file mode 100644@@@ -0,0 +1,75 @@++{++ "version": "0.1.0",++ "contractRanges": {++ "openvibe-contracts": ">=0.116.0 <1.0.0"++ },++ "id": "help",++ "name": "OpenVibe.Help",++ "status": "alpha",++ "repository": "OpenVibers/OpenVibe.Help",++ "domains": [++ "openvibe.help"++ ],++ "publicOrigin": "https://openvibe.help",++ "exposure": {++ "state": "live",++ "publicSite": "service"++ },++ "capabilities": [],++ "eventsProduced": [],++ "eventsConsumed": [],++ "namespacesOwned": [++ "help.*"++ ],++ "lifecycle": {++ "liveness": {++ "endpoint": "/api/health",++ "means": "the process is up and answers HTTP; it checks nothing else (the database is what ready checks)"++ },++ "shutdown": {++ "signal": "SIGTERM",++ "deadlineSeconds": 10,++ "drains": [++ "HTTP server closed",++ "Network key refresher stopped",++ "store closed"++ ]++ },++ "startupRecovery": {++ "resumes": [++ {++ "kind": "sessions",++ "what": "sign-in sessions are rows in the database, so a person stays signed in across the restart; nothing runs in the background that a restart could leave half done"++ }++ ]++ },++ "rollback": {++ "conditions": [++ "automatic: ovhost deploy puts the previous sha back and restarts again when the ready URL does not answer 2xx within ready.timeoutSeconds after the restart (exit 3)",++ "manual: ovhost rollback <service> --to <sha>"++ ],++ "window": "automatic only while ovhost waits for readiness after the restart; manual afterwards, as long as no blocker applies",++ "blockers": {++ "none": "migrations 0001-0002 only create Help's own tables; an earlier release has none to read"++ }++ },++ "contracts": {++ "range": ">=0.116.0 <1.0.0"++ },++ "leases": {++ "none": "no claims: one process serves the site"++ }++ },++ "notes": "Port 5020, unit openvibe-help.service. The network's help centre: a catalog of every OpenVibe product site built from openvibe-contracts (each site's state, one line, pillars and FAQ), articles generated from it, search across them, and support tickets a signed-in person opens and staff answer (staff judged by the Network role claim). Consumes OpenVibe.Network (SSO with PKCE S256 as OAuth client help, JWKS). Owns help_tickets and help_messages. No capabilities yet: tickets are a person's own, not an app's.",++ "health": "/api/health",++ "ready": "/api/ready",++ "internalOrigin": "http://127.0.0.1:5020",++ "site": {++ "name": "Help",++ "icon": "help",++ "tagline": "Answers for every OpenVibe site",++ "what": "Every OpenVibe site in one help centre: what each one does, its common questions, and a ticket to a person when the answer is not there.",++ "legalProfile": "account",++ "position": 25++ }++}nnew file mode 100644@@@ -0,0 +1,76 @@++{++ "version": "0.1.0",++ "contractRanges": {++ "openvibe-contracts": ">=0.116.0 <1.0.0"++ },++ "id": "work",++ "name": "OpenVibe.Work",++ "status": "alpha",++ "repository": "OpenVibers/OpenVibe.Work",++ "domains": [++ "openvibe.work"++ ],++ "publicOrigin": "https://openvibe.work",++ "exposure": {++ "state": "live",++ "publicSite": "service"++ },++ "capabilities": [],++ "eventsProduced": [],++ "eventsConsumed": [],++ "namespacesOwned": [++ "work.*"++ ],++ "lifecycle": {++ "liveness": {++ "endpoint": "/api/health",++ "means": "the process is up and answers HTTP; it checks nothing else (the database is what ready checks)"++ },++ "shutdown": {++ "signal": "SIGTERM",++ "deadlineSeconds": 10,++ "drains": [++ "HTTP server closed",++ "ingest timer stopped (a fetch in flight is abandoned; its rows are written per listing)",++ "Network key refresher stopped",++ "store closed"++ ]++ },++ "startupRecovery": {++ "resumes": [++ {++ "kind": "jobs",++ "what": "the ingest starts again on its timer; each source is fetched when its last fetch is older than 6 hours, so a fetch cut short by a restart is simply redone"++ }++ ]++ },++ "rollback": {++ "conditions": [++ "automatic: ovhost deploy puts the previous sha back and restarts again when the ready URL does not answer 2xx within ready.timeoutSeconds after the restart (exit 3)",++ "manual: ovhost rollback <service> --to <sha>"++ ],++ "window": "automatic only while ovhost waits for readiness after the restart; manual afterwards, as long as no blocker applies",++ "blockers": {++ "none": "migrations 0001-0002 only create Work's own tables; an earlier release has none to read"++ }++ },++ "contracts": {++ "range": ">=0.116.0 <1.0.0"++ },++ "leases": {++ "none": "no claims: one process serves the site"++ }++ },++ "notes": "Port 4960, unit openvibe-work.service. Job listings gathered from open job-board APIs (Arbeitnow, Remotive, RemoteOK) with their provenance: a short plain-text excerpt only, the source named and the original listing linked on every list and page, never the full description. Search with remote/type/location filters, JobPosting JSON-LD, and saved searches with a count of what is new since you last looked. The ingest runs every 6 hours per source when WORK_INGEST=on, upserts by (source, source_id) and expires listings not seen for 30 days. Consumes OpenVibe.Network (SSO with PKCE S256 as OAuth client work, JWKS). Owns work_listings, work_source_fetches and work_saved_searches. No capabilities yet: saved searches are a person's, listings are public.",++ "health": "/api/health",++ "ready": "/api/ready",++ "internalOrigin": "http://127.0.0.1:4960",++ "site": {++ "name": "Work",++ "icon": "work",++ "tagline": "Find work, with an agent on your side",++ "what": "Job listings from open job boards in one search, each linked back to where you apply, with saved searches that tell you what is new.",++ "legalProfile": "info",++ "position": 26++ }++}@@@ -1,6 +1,6 @@{"name": "openvibe-contracts",-- "version": "0.115.0",++ "version": "0.116.0","description": "Machine-readable contracts for the OpenVibe network: schemas, manifests, ids, error envelope, capability checks.","main": "index.js","types": "generated/typescript/index.d.ts",
@@ -4,6 +4,21 @@ All notable changes to `openvibe-contracts`. Releases are git tags (`vX.Y.Z`) th
@@ -4,6 +4,21 @@ All notable changes to `openvibe-contracts`. Releases are git tags (`vX.Y.Z`) th
from `https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/<tag>`. Before v0.30.0,
from `https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/<tag>`. Before v0.30.0,
the notes were in the tag and commit messages (`git tag -n1`).
the notes were in the tag and commit messages (`git tag -n1`).
+## 0.116.0 — 2026-10-08
+
+**OpenVibe.Food, OpenVibe.Help and OpenVibe.Work become services** (plan T19 "flesh the network out"; owner 2026-10-08).
+- `manifests/services/food.json` (port 4970, `openvibe.food` live, site position 24): food banks and budget grocers
+ near you from OpenStreetMap (ODbL attribution on every answer), the budget food list, meal plans and a pantry.
+- `manifests/services/help.json` (port 5020, `openvibe.help` live, position 25): the network's help centre built from
+ this repository's catalog, and support tickets. No capabilities: a ticket is a person's own.
+- `manifests/services/work.json` (port 4960, `openvibe.work` live, position 26): job listings from open job boards with
+ their provenance (excerpt only, original linked), search and saved searches. No capabilities yet.
+- Capabilities (audience `openvibe.food`): `food.plan.write` (sensitive), `food.plan.read`, `food.pantry.write`
+ (sensitive), `food.pantry.read`; the place search and the food list are public reads with no capability.
+- Schemas: `food.plan-create-request@1`, `food.plan@1`, `food.plan-result@1`, `food.pantry-request@1`,
+ `food.pantry-result@1`.
+- The three product manifests point at their services.
+
## 0.115.0 — 2026-10-08
## 0.115.0 — 2026-10-08
**OpenVibe.Actor becomes a service** (plan T17, ADR-044; owner direction 2026-10-08: Actor is OpenVibe's own general agent
**OpenVibe.Actor becomes a service** (plan T17, ADR-044; owner direction 2026-10-08: Actor is OpenVibe's own general agent
@@ -6353,6 +6353,51 @@
@@ -6353,6 +6353,51 @@
"compatibility": "backward",
"compatibility": "backward",
"status": "active",
"status": "active",
"adr": "ADR-044"
"adr": "ADR-044"
+ },
+ {
+ "id": "food.pantry-request",
+ "version": "1.0.0",
+ "owner": "food",
+ "schema": "food/pantry-request.v1.json",
+ "visibility": "public",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "food.pantry-result",
+ "version": "1.0.0",
+ "owner": "food",
+ "schema": "food/pantry-result.v1.json",
+ "visibility": "public",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "food.plan",
+ "version": "1.0.0",
+ "owner": "food",
+ "schema": "food/plan.v1.json",
+ "visibility": "public",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "food.plan-create-request",
+ "version": "1.0.0",
+ "owner": "food",
+ "schema": "food/plan-create-request.v1.json",
+ "visibility": "public",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "food.plan-result",
+ "version": "1.0.0",
+ "owner": "food",
+ "schema": "food/plan-result.v1.json",
+ "visibility": "public",
+ "compatibility": "backward",
+ "status": "active"
}
}
]
]
}
}
new file mode 100644
new file mode 100644
@@ -0,0 +1,22 @@
@@ -0,0 +1,22 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/food/pantry-request.v1.json",
+ "title": "FoodPantryRequest",
+ "description": "food.pantry-request@1: PUT /api/v1/pantry on OpenVibe.Food replaces the pantry with exactly these food slugs.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "items"
+ ],
+ "properties": {
+ "items": {
+ "type": "array",
+ "maxItems": 200,
+ "items": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 80
+ }
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,110 @@
@@ -0,0 +1,110 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/food/pantry-result.v1.json",
+ "title": "FoodPantryResult",
+ "description": "food.pantry-result@1: GET and PUT /api/v1/pantry on OpenVibe.Food: the slugs, each food as the food list shows it, and the suggestions matched against them (can_make: everything there; almost: one or two items missing, named).",
+ "type": "object",
+ "required": [
+ "items",
+ "foods",
+ "suggestions"
+ ],
+ "properties": {
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "foods": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "slug",
+ "name"
+ ],
+ "properties": {
+ "slug": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "suggestions": {
+ "type": "object",
+ "required": [
+ "can_make",
+ "almost"
+ ],
+ "properties": {
+ "can_make": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "slug",
+ "name"
+ ],
+ "properties": {
+ "slug": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "serves": {
+ "type": "integer"
+ },
+ "minutes": {
+ "type": "integer"
+ },
+ "missing": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ },
+ "almost": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "slug",
+ "name"
+ ],
+ "properties": {
+ "slug": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "serves": {
+ "type": "integer"
+ },
+ "minutes": {
+ "type": "integer"
+ },
+ "missing": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "note": {
+ "type": "string"
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,39 @@
@@ -0,0 +1,39 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/food/plan-create-request.v1.json",
+ "title": "FoodPlanCreateRequest",
+ "description": "food.plan-create-request@1: POST /api/v1/plans on OpenVibe.Food. people 1-12, days 1-14, budget (US dollars for the whole plan) optional, title optional.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "people",
+ "days"
+ ],
+ "properties": {
+ "people": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 12
+ },
+ "days": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 14
+ },
+ "budget": {
+ "type": [
+ "number",
+ "null"
+ ],
+ "minimum": 1,
+ "maximum": 5000
+ },
+ "title": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 120
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,26 @@
@@ -0,0 +1,26 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/food/plan-result.v1.json",
+ "title": "FoodPlanResult",
+ "description": "food.plan-result@1: answers of the plan routes on OpenVibe.Food: GET /api/v1/plans → { plans }; GET /api/v1/plans/:id and POST /api/v1/plans (201) → the plan itself (food.plan@1).",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "plans"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "plans": {
+ "type": "array",
+ "items": {
+ "$ref": "plan.v1.json"
+ }
+ }
+ }
+ },
+ {
+ "$ref": "plan.v1.json"
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,115 @@
@@ -0,0 +1,115 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/food/plan.v1.json",
+ "title": "FoodPlan",
+ "description": "food.plan@1: one saved plan on OpenVibe.Food.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "id",
+ "title",
+ "people",
+ "days",
+ "budget",
+ "created_at",
+ "plan"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^pln_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "title": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 120
+ },
+ "people": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "days": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "budget": {
+ "type": [
+ "number",
+ "null"
+ ],
+ "minimum": 0
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "plan": {
+ "type": "object",
+ "description": "The plan as Food worked it out: spec, items (the shopping list), by_store, menu (one entry a day: breakfast, lunch, dinner, snack), group_coverage and totals (cost, cost_per_person_day, kcal, protein). Food may add fields.",
+ "required": [
+ "spec",
+ "items",
+ "menu",
+ "totals"
+ ],
+ "properties": {
+ "spec": {
+ "type": "object",
+ "required": [
+ "people",
+ "days"
+ ],
+ "properties": {
+ "people": {
+ "type": "integer"
+ },
+ "days": {
+ "type": "integer"
+ },
+ "budget": {
+ "type": [
+ "number",
+ "null"
+ ]
+ }
+ }
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object"
+ }
+ },
+ "menu": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "day"
+ ],
+ "properties": {
+ "day": {
+ "type": "integer",
+ "minimum": 1
+ }
+ }
+ }
+ },
+ "totals": {
+ "type": "object",
+ "required": [
+ "cost"
+ ],
+ "properties": {
+ "cost": {
+ "type": "number",
+ "minimum": 0
+ }
+ }
+ }
+ }
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
+{
+ "items": [
+ ""
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
+{
+ "items": "eggs"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
+{
+ "items": []
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
+{
+ "items": [
+ "eggs-dozen",
+ "rice-long-grain-5lb"
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
+{
+ "items": [],
+ "foods": []
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,26 @@
@@ -0,0 +1,26 @@
+{
+ "items": [
+ "eggs-dozen"
+ ],
+ "foods": [
+ {
+ "slug": "eggs-dozen",
+ "name": "Eggs, dozen"
+ }
+ ],
+ "suggestions": {
+ "can_make": [],
+ "almost": [
+ {
+ "slug": "egg-fried-rice",
+ "name": "Egg fried rice",
+ "serves": 2,
+ "minutes": 15,
+ "missing": [
+ "Rice"
+ ]
+ }
+ ]
+ },
+ "note": "Basic suggestions."
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
+{
+ "people": 2,
+ "days": 2,
+ "owner": "someone"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
+{
+ "people": 2
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
+{
+ "people": 40,
+ "days": 3
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
+{
+ "people": 4,
+ "days": 7,
+ "budget": 120,
+ "title": "Family week"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
+{
+ "people": 1,
+ "days": 3
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,41 @@
@@ -0,0 +1,41 @@
+{
+ "plans": {
+ "id": "pln_01JZ6Q4X8M2N3P4R5S6T7V8W9X",
+ "title": "Week for two",
+ "people": 2,
+ "days": 7,
+ "budget": 80,
+ "created_at": "2026-10-08T20:00:00.000Z",
+ "plan": {
+ "spec": {
+ "people": 2,
+ "days": 7,
+ "budget": 80
+ },
+ "items": [
+ {
+ "slug": "rice-long-grain-5lb",
+ "name": "Long grain rice, 5 lb",
+ "packs": 1
+ }
+ ],
+ "menu": [
+ {
+ "day": 1,
+ "breakfast": [
+ "Oats"
+ ],
+ "lunch": [],
+ "dinner": [
+ "Rice"
+ ],
+ "snack": []
+ }
+ ],
+ "totals": {
+ "cost": 41.2,
+ "kcal": 28000
+ }
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
+{
+ "plans": []
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,43 @@
@@ -0,0 +1,43 @@
+{
+ "plans": [
+ {
+ "id": "pln_01JZ6Q4X8M2N3P4R5S6T7V8W9X",
+ "title": "Week for two",
+ "people": 2,
+ "days": 7,
+ "budget": 80,
+ "created_at": "2026-10-08T20:00:00.000Z",
+ "plan": {
+ "spec": {
+ "people": 2,
+ "days": 7,
+ "budget": 80
+ },
+ "items": [
+ {
+ "slug": "rice-long-grain-5lb",
+ "name": "Long grain rice, 5 lb",
+ "packs": 1
+ }
+ ],
+ "menu": [
+ {
+ "day": 1,
+ "breakfast": [
+ "Oats"
+ ],
+ "lunch": [],
+ "dinner": [
+ "Rice"
+ ],
+ "snack": []
+ }
+ ],
+ "totals": {
+ "cost": 41.2,
+ "kcal": 28000
+ }
+ }
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,39 @@
@@ -0,0 +1,39 @@
+{
+ "id": "pln_01JZ6Q4X8M2N3P4R5S6T7V8W9X",
+ "title": "Week for two",
+ "people": 2,
+ "days": 7,
+ "budget": 80,
+ "created_at": "2026-10-08T20:00:00.000Z",
+ "plan": {
+ "spec": {
+ "people": 2,
+ "days": 7,
+ "budget": 80
+ },
+ "items": [
+ {
+ "slug": "rice-long-grain-5lb",
+ "name": "Long grain rice, 5 lb",
+ "packs": 1
+ }
+ ],
+ "menu": [
+ {
+ "day": 1,
+ "breakfast": [
+ "Oats"
+ ],
+ "lunch": [],
+ "dinner": [
+ "Rice"
+ ],
+ "snack": []
+ }
+ ],
+ "totals": {
+ "cost": 41.2,
+ "kcal": 28000
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,39 @@
@@ -0,0 +1,39 @@
+{
+ "id": "plan-1",
+ "title": "Week for two",
+ "people": 2,
+ "days": 7,
+ "budget": 80,
+ "created_at": "2026-10-08T20:00:00.000Z",
+ "plan": {
+ "spec": {
+ "people": 2,
+ "days": 7,
+ "budget": 80
+ },
+ "items": [
+ {
+ "slug": "rice-long-grain-5lb",
+ "name": "Long grain rice, 5 lb",
+ "packs": 1
+ }
+ ],
+ "menu": [
+ {
+ "day": 1,
+ "breakfast": [
+ "Oats"
+ ],
+ "lunch": [],
+ "dinner": [
+ "Rice"
+ ],
+ "snack": []
+ }
+ ],
+ "totals": {
+ "cost": 41.2,
+ "kcal": 28000
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
+{
+ "id": "pln_01JZ6Q4X8M2N3P4R5S6T7V8W9X",
+ "title": "Week for two",
+ "people": 2,
+ "days": 7,
+ "budget": 80,
+ "created_at": "2026-10-08T20:00:00.000Z"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,39 @@
@@ -0,0 +1,39 @@
+{
+ "id": "pln_01JZ6Q4X8M2N3P4R5S6T7V8W9X",
+ "title": "Week for two",
+ "people": 2,
+ "days": 7,
+ "budget": 80,
+ "created_at": "2026-10-08T20:00:00.000Z",
+ "plan": {
+ "spec": {
+ "people": 2,
+ "days": 7,
+ "budget": 80
+ },
+ "items": [
+ {
+ "slug": "rice-long-grain-5lb",
+ "name": "Long grain rice, 5 lb",
+ "packs": 1
+ }
+ ],
+ "menu": [
+ {
+ "day": 1,
+ "breakfast": [
+ "Oats"
+ ],
+ "lunch": [],
+ "dinner": [
+ "Rice"
+ ],
+ "snack": []
+ }
+ ],
+ "totals": {
+ "cost": 41.2,
+ "kcal": 28000
+ }
+ }
+}
@@ -73477,6 +73477,343 @@
@@ -73477,6 +73477,343 @@
}
}
}
}
}
}
+ },
+ "food.pantry-request": {
+ "version": "1.0.0",
+ "owner": "food",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/food/pantry-request.v1.json",
+ "title": "FoodPantryRequest",
+ "description": "food.pantry-request@1: PUT /api/v1/pantry on OpenVibe.Food replaces the pantry with exactly these food slugs.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "items"
+ ],
+ "properties": {
+ "items": {
+ "type": "array",
+ "maxItems": 200,
+ "items": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 80
+ }
+ }
+ }
+ }
+ },
+ "food.pantry-result": {
+ "version": "1.0.0",
+ "owner": "food",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/food/pantry-result.v1.json",
+ "title": "FoodPantryResult",
+ "description": "food.pantry-result@1: GET and PUT /api/v1/pantry on OpenVibe.Food: the slugs, each food as the food list shows it, and the suggestions matched against them (can_make: everything there; almost: one or two items missing, named).",
+ "type": "object",
+ "required": [
+ "items",
+ "foods",
+ "suggestions"
+ ],
+ "properties": {
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "foods": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "slug",
+ "name"
+ ],
+ "properties": {
+ "slug": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "suggestions": {
+ "type": "object",
+ "required": [
+ "can_make",
+ "almost"
+ ],
+ "properties": {
+ "can_make": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "slug",
+ "name"
+ ],
+ "properties": {
+ "slug": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "serves": {
+ "type": "integer"
+ },
+ "minutes": {
+ "type": "integer"
+ },
+ "missing": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ },
+ "almost": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "slug",
+ "name"
+ ],
+ "properties": {
+ "slug": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "serves": {
+ "type": "integer"
+ },
+ "minutes": {
+ "type": "integer"
+ },
+ "missing": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "note": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "food.plan": {
+ "version": "1.0.0",
+ "owner": "food",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/food/plan.v1.json",
+ "title": "FoodPlan",
+ "description": "food.plan@1: one saved plan on OpenVibe.Food.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "id",
+ "title",
+ "people",
+ "days",
+ "budget",
+ "created_at",
+ "plan"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^pln_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "title": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 120
+ },
+ "people": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "days": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "budget": {
+ "type": [
+ "number",
+ "null"
+ ],
+ "minimum": 0
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "plan": {
+ "type": "object",
+ "description": "The plan as Food worked it out: spec, items (the shopping list), by_store, menu (one entry a day: breakfast, lunch, dinner, snack), group_coverage and totals (cost, cost_per_person_day, kcal, protein). Food may add fields.",
+ "required": [
+ "spec",
+ "items",
+ "menu",
+ "totals"
+ ],
+ "properties": {
+ "spec": {
+ "type": "object",
+ "required": [
+ "people",
+ "days"
+ ],
+ "properties": {
+ "people": {
+ "type": "integer"
+ },
+ "days": {
+ "type": "integer"
+ },
+ "budget": {
+ "type": [
+ "number",
+ "null"
+ ]
+ }
+ }
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object"
+ }
+ },
+ "menu": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "day"
+ ],
+ "properties": {
+ "day": {
+ "type": "integer",
+ "minimum": 1
+ }
+ }
+ }
+ },
+ "totals": {
+ "type": "object",
+ "required": [
+ "cost"
+ ],
+ "properties": {
+ "cost": {
+ "type": "number",
+ "minimum": 0
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "food.plan-create-request": {
+ "version": "1.0.0",
+ "owner": "food",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/food/plan-create-request.v1.json",
+ "title": "FoodPlanCreateRequest",
+ "description": "food.plan-create-request@1: POST /api/v1/plans on OpenVibe.Food. people 1-12, days 1-14, budget (US dollars for the whole plan) optional, title optional.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "people",
+ "days"
+ ],
+ "properties": {
+ "people": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 12
+ },
+ "days": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 14
+ },
+ "budget": {
+ "type": [
+ "number",
+ "null"
+ ],
+ "minimum": 1,
+ "maximum": 5000
+ },
+ "title": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 120
+ }
+ }
+ }
+ },
+ "food.plan-result": {
+ "version": "1.0.0",
+ "owner": "food",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/food/plan-result.v1.json",
+ "title": "FoodPlanResult",
+ "description": "food.plan-result@1: answers of the plan routes on OpenVibe.Food: GET /api/v1/plans → { plans }; GET /api/v1/plans/:id and POST /api/v1/plans (201) → the plan itself (food.plan@1).",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "plans"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "plans": {
+ "type": "array",
+ "items": {
+ "$ref": "plan.v1.json"
+ }
+ }
+ }
+ },
+ {
+ "$ref": "plan.v1.json"
+ }
+ ]
+ }
}
}
}
}
}
}
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Actor API",
"title": "OpenVibe.Actor API",
- "version": "0.115.0",
- "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",
+ "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.AI API",
"title": "OpenVibe.AI API",
- "version": "0.115.0",
- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",
+ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Billing API",
"title": "OpenVibe.Billing API",
- "version": "0.115.0",
- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",
+ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Blog API",
"title": "OpenVibe.Blog API",
- "version": "0.115.0",
- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",
+ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Bot API",
"title": "OpenVibe.Bot API",
- "version": "0.115.0",
- "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",
+ "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Chat API",
"title": "OpenVibe.Chat API",
- "version": "0.115.0",
- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",
+ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Community API",
"title": "OpenVibe.Community API",
- "version": "0.115.0",
- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",
+ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Coupons API",
"title": "OpenVibe.Coupons API",
- "version": "0.115.0",
- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",
+ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Deals API",
"title": "OpenVibe.Deals API",
- "version": "0.115.0",
- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",
+ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Events API",
"title": "OpenVibe.Events API",
- "version": "0.115.0",
- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",
+ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
new file mode 100644
new file mode 100644
@@ -0,0 +1,656 @@
@@ -0,0 +1,656 @@
+{
+ "openapi": "3.1.0",
+ "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
+ "info": {
+ "title": "OpenVibe.Food API",
+ "version": "0.116.0",
+ "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "license": {
+ "name": "MIT",
+ "identifier": "MIT"
+ }
+ },
+ "servers": [
+ {
+ "url": "https://openvibe.food"
+ }
+ ],
+ "paths": {
+ "/api/v1/pantry": {
+ "get": {
+ "operationId": "get_api_v1_pantry",
+ "summary": "Read a caller's pantry with the suggestions matched against it: GET /api/v1/pantry → { items, foods, suggestions: { can_make, almost }, note }.",
+ "description": "**food.pantry.read** (public): Read a caller's pantry with the suggestions matched against it: GET /api/v1/pantry → { items, foods, suggestions: { can_make, almost }, note }. The suggestions are deterministic matches against basic recipes, not dietary advice.",
+ "tags": [
+ "food.pantry"
+ ],
+ "x-openvibe-capabilities": [
+ "food.pantry.read"
+ ],
+ "x-openvibe-visibility": [
+ "public"
+ ],
+ "x-openvibe-quota-class": "food-read",
+ "security": [
+ {
+ "openvibeToken": [
+ "food.pantry.read"
+ ]
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/food.pantry-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "put": {
+ "operationId": "put_api_v1_pantry",
+ "summary": "Replace a caller's pantry with exactly this list: PUT /api/v1/pantry with { items: [food slug, …] } (at most 200, each one in the food list; GET /api/v1/foods lists them).",
+ "description": "**food.pantry.write** (public): Replace a caller's pantry with exactly this list: PUT /api/v1/pantry with { items: [food slug, …] } (at most 200, each one in the food list; GET /api/v1/foods lists them). Answers like a read.",
+ "tags": [
+ "food.pantry"
+ ],
+ "x-openvibe-capabilities": [
+ "food.pantry.write"
+ ],
+ "x-openvibe-visibility": [
+ "public"
+ ],
+ "x-openvibe-quota-class": "food-write",
+ "security": [
+ {
+ "openvibeToken": [
+ "food.pantry.write"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/food.pantry-request.v1"
+ }
+ }
+ }
+ },
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/food.pantry-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/plans": {
+ "get": {
+ "operationId": "get_api_v1_plans",
+ "summary": "Read the plans a caller saved: GET /api/v1/plans (newest first, ?limit to 50) → { plans }, and GET /api/v1/plans/:id → one plan.",
+ "description": "**food.plan.read** (public): Read the plans a caller saved: GET /api/v1/plans (newest first, ?limit to 50) → { plans }, and GET /api/v1/plans/:id → one plan. Only the person who saved a plan (or their tokens) can read it.",
+ "tags": [
+ "food.plan"
+ ],
+ "x-openvibe-capabilities": [
+ "food.plan.read"
+ ],
+ "x-openvibe-visibility": [
+ "public"
+ ],
+ "x-openvibe-quota-class": "food-read",
+ "security": [
+ {
+ "openvibeToken": [
+ "food.plan.read"
+ ]
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/food.plan-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "post": {
+ "operationId": "post_api_v1_plans",
+ "summary": "Plan a shop for a household and a budget and save it: POST /api/v1/plans with { people, days, budget?, title?",
+ "description": "**food.plan.write** (public): Plan a shop for a household and a budget and save it: POST /api/v1/plans with { people, days, budget?, title? }. Food picks budget items from its food list to cover each food group for every person and day, splits them by the cheapest store and lays out a menu; the saved plan (201, Location /api/v1/plans/:id) is the caller's own.",
+ "tags": [
+ "food.plan"
+ ],
+ "x-openvibe-capabilities": [
+ "food.plan.write"
+ ],
+ "x-openvibe-visibility": [
+ "public"
+ ],
+ "x-openvibe-quota-class": "food-write",
+ "security": [
+ {
+ "openvibeToken": [
+ "food.plan.write"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/food.plan-create-request.v1"
+ }
+ }
+ }
+ },
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/food.plan-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/plans/{id}": {
+ "get": {
+ "operationId": "get_api_v1_plans_id",
+ "summary": "Read the plans a caller saved: GET /api/v1/plans (newest first, ?limit to 50) → { plans }, and GET /api/v1/plans/:id → one plan.",
+ "description": "**food.plan.read** (public): Read the plans a caller saved: GET /api/v1/plans (newest first, ?limit to 50) → { plans }, and GET /api/v1/plans/:id → one plan. Only the person who saved a plan (or their tokens) can read it.",
+ "tags": [
+ "food.plan"
+ ],
+ "x-openvibe-capabilities": [
+ "food.plan.read"
+ ],
+ "x-openvibe-visibility": [
+ "public"
+ ],
+ "x-openvibe-quota-class": "food-read",
+ "security": [
+ {
+ "openvibeToken": [
+ "food.plan.read"
+ ]
+ }
+ ],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/food.plan-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "errors.problem.v1": {
+ "title": "Problem",
+ "description": "RFC 9457 problem details (application/problem+json) with a stable OpenVibe error code and trace identifiers. The legacy 'error' string is allowed during migration so existing clients keep working.",
+ "type": "object",
+ "required": [
+ "type",
+ "title",
+ "status",
+ "code"
+ ],
+ "additionalProperties": true,
+ "properties": {
+ "type": {
+ "type": "string",
+ "format": "uri-reference"
+ },
+ "title": {
+ "type": "string",
+ "minLength": 1
+ },
+ "status": {
+ "type": "integer",
+ "minimum": 400,
+ "maximum": 599
+ },
+ "detail": {
+ "type": "string"
+ },
+ "instance": {
+ "type": "string",
+ "format": "uri-reference"
+ },
+ "code": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)*$"
+ },
+ "request_id": {
+ "type": "string"
+ },
+ "trace_id": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{32}$"
+ },
+ "errors": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "message"
+ ],
+ "properties": {
+ "path": {
+ "type": "string"
+ },
+ "message": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "error": {
+ "type": "string",
+ "description": "Deprecated compatibility field: same text as title/detail for clients that read {error}."
+ }
+ }
+ },
+ "food.pantry-request.v1": {
+ "title": "FoodPantryRequest",
+ "description": "food.pantry-request@1: PUT /api/v1/pantry on OpenVibe.Food replaces the pantry with exactly these food slugs.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "items"
+ ],
+ "properties": {
+ "items": {
+ "type": "array",
+ "maxItems": 200,
+ "items": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 80
+ }
+ }
+ }
+ },
+ "food.pantry-result.v1": {
+ "title": "FoodPantryResult",
+ "description": "food.pantry-result@1: GET and PUT /api/v1/pantry on OpenVibe.Food: the slugs, each food as the food list shows it, and the suggestions matched against them (can_make: everything there; almost: one or two items missing, named).",
+ "type": "object",
+ "required": [
+ "items",
+ "foods",
+ "suggestions"
+ ],
+ "properties": {
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "foods": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "slug",
+ "name"
+ ],
+ "properties": {
+ "slug": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "suggestions": {
+ "type": "object",
+ "required": [
+ "can_make",
+ "almost"
+ ],
+ "properties": {
+ "can_make": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "slug",
+ "name"
+ ],
+ "properties": {
+ "slug": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "serves": {
+ "type": "integer"
+ },
+ "minutes": {
+ "type": "integer"
+ },
+ "missing": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ },
+ "almost": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "slug",
+ "name"
+ ],
+ "properties": {
+ "slug": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "serves": {
+ "type": "integer"
+ },
+ "minutes": {
+ "type": "integer"
+ },
+ "missing": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "note": {
+ "type": "string"
+ }
+ }
+ },
+ "food.plan-create-request.v1": {
+ "title": "FoodPlanCreateRequest",
+ "description": "food.plan-create-request@1: POST /api/v1/plans on OpenVibe.Food. people 1-12, days 1-14, budget (US dollars for the whole plan) optional, title optional.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "people",
+ "days"
+ ],
+ "properties": {
+ "people": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 12
+ },
+ "days": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 14
+ },
+ "budget": {
+ "type": [
+ "number",
+ "null"
+ ],
+ "minimum": 1,
+ "maximum": 5000
+ },
+ "title": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 120
+ }
+ }
+ },
+ "food.plan-result.v1": {
+ "title": "FoodPlanResult",
+ "description": "food.plan-result@1: answers of the plan routes on OpenVibe.Food: GET /api/v1/plans → { plans }; GET /api/v1/plans/:id and POST /api/v1/plans (201) → the plan itself (food.plan@1).",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "plans"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "plans": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/food.plan.v1"
+ }
+ }
+ }
+ },
+ {
+ "$ref": "#/components/schemas/food.plan.v1"
+ }
+ ]
+ },
+ "food.plan.v1": {
+ "title": "FoodPlan",
+ "description": "food.plan@1: one saved plan on OpenVibe.Food.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "id",
+ "title",
+ "people",
+ "days",
+ "budget",
+ "created_at",
+ "plan"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^pln_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "title": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 120
+ },
+ "people": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "days": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "budget": {
+ "type": [
+ "number",
+ "null"
+ ],
+ "minimum": 0
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "plan": {
+ "type": "object",
+ "description": "The plan as Food worked it out: spec, items (the shopping list), by_store, menu (one entry a day: breakfast, lunch, dinner, snack), group_coverage and totals (cost, cost_per_person_day, kcal, protein). Food may add fields.",
+ "required": [
+ "spec",
+ "items",
+ "menu",
+ "totals"
+ ],
+ "properties": {
+ "spec": {
+ "type": "object",
+ "required": [
+ "people",
+ "days"
+ ],
+ "properties": {
+ "people": {
+ "type": "integer"
+ },
+ "days": {
+ "type": "integer"
+ },
+ "budget": {
+ "type": [
+ "number",
+ "null"
+ ]
+ }
+ }
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object"
+ }
+ },
+ "menu": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "day"
+ ],
+ "properties": {
+ "day": {
+ "type": "integer",
+ "minimum": 1
+ }
+ }
+ }
+ },
+ "totals": {
+ "type": "object",
+ "required": [
+ "cost"
+ ],
+ "properties": {
+ "cost": {
+ "type": "number",
+ "minimum": 0
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "securitySchemes": {
+ "openvibeToken": {
+ "type": "http",
+ "scheme": "bearer",
+ "bearerFormat": "JWT",
+ "description": "An OpenVibe Network token for this service: a person's token, an app token (client credentials, audience of the service) or a first-party service token. Its `cap` claim must include the capability the operation names."
+ }
+ }
+ }
+}
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Games API",
"title": "OpenVibe.Games API",
- "version": "0.115.0",
- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",
+ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Host API",
"title": "OpenVibe.Host API",
- "version": "0.115.0",
- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",
+ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -71,6 +71,13 @@
@@ -71,6 +71,13 @@
"operations": 15,
"operations": 15,
"capabilities": 8
"capabilities": 8
},
},
+ {
+ "service": "food",
+ "name": "OpenVibe.Food",
+ "origin": "https://openvibe.food",
+ "operations": 5,
+ "capabilities": 4
+ },
{
{
"service": "games",
"service": "games",
"name": "OpenVibe.Games",
"name": "OpenVibe.Games",
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Live API",
"title": "OpenVibe.Live API",
- "version": "0.115.0",
- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",
+ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Media API",
"title": "OpenVibe.Media API",
- "version": "0.115.0",
- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",
+ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Network API",
"title": "OpenVibe.Network API",
- "version": "0.115.0",
- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",
+ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.News API",
"title": "OpenVibe.News API",
- "version": "0.115.0",
- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",
+ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenRe.Stream API",
"title": "OpenRe.Stream API",
- "version": "0.115.0",
- "description": "Routes of OpenRe.Stream as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",
+ "description": "Routes of OpenRe.Stream as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Reviews API",
"title": "OpenVibe.Reviews API",
- "version": "0.115.0",
- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",
+ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Run API",
"title": "OpenVibe.Run API",
- "version": "0.115.0",
- "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",
+ "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Search API",
"title": "OpenVibe.Search API",
- "version": "0.115.0",
- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",
+ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Services API",
"title": "OpenVibe.Services API",
- "version": "0.115.0",
- "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",
+ "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Sources API",
"title": "OpenVibe.Sources API",
- "version": "0.115.0",
- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",
+ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Space API",
"title": "OpenVibe.Space API",
- "version": "0.115.0",
- "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",
+ "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Tips API",
"title": "OpenVibe.Tips API",
- "version": "0.115.0",
- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",
+ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Tools API",
"title": "OpenVibe.Tools API",
- "version": "0.115.0",
- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",
+ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Trade API",
"title": "OpenVibe.Trade API",
- "version": "0.115.0",
- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",
+ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.VIP API",
"title": "OpenVibe.VIP API",
- "version": "0.115.0",
- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",
+ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Watch API",
"title": "OpenVibe.Watch API",
- "version": "0.115.0",
- "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",
+ "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Wiki API",
"title": "OpenVibe.Wiki API",
- "version": "0.115.0",
- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.115.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.116.0",
+ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.116.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -58953,3 +58953,94 @@ export interface ActorAgentListResult {
@@ -58953,3 +58953,94 @@ export interface ActorAgentListResult {
reason?: string;
reason?: string;
}[];
}[];
}
}
+
+/** [email protected] (owner: food) */
+/**
+ * food.pantry-request@1: PUT /api/v1/pantry on OpenVibe.Food replaces the pantry with exactly these food slugs.
+ */
+export interface FoodPantryRequest {
+ /**
+ * @maxItems 200
+ */
+ items: string[];
+}
+
+/** [email protected] (owner: food) */
+/**
+ * food.pantry-result@1: GET and PUT /api/v1/pantry on OpenVibe.Food: the slugs, each food as the food list shows it, and the suggestions matched against them (can_make: everything there; almost: one or two items missing, named).
+ */
+export interface FoodPantryResult {
+ items: string[];
+ foods: {
+ slug: string;
+ name: string;
+ }[];
+ suggestions: {
+ can_make: {
+ slug: string;
+ name: string;
+ serves?: number;
+ minutes?: number;
+ missing?: string[];
+ }[];
+ almost: {
+ slug: string;
+ name: string;
+ serves?: number;
+ minutes?: number;
+ missing?: string[];
+ }[];
+ };
+ note?: string;
+}
+
+/** [email protected] (owner: food) */
+/**
+ * food.plan@1: one saved plan on OpenVibe.Food.
+ */
+export interface FoodPlan {
+ id: string;
+ title: string | null;
+ people: number;
+ days: number;
+ budget: number | null;
+ created_at: string;
+ /**
+ * The plan as Food worked it out: spec, items (the shopping list), by_store, menu (one entry a day: breakfast, lunch, dinner, snack), group_coverage and totals (cost, cost_per_person_day, kcal, protein). Food may add fields.
+ */
+ plan: {
+ spec: {
+ people: number;
+ days: number;
+ budget?: number | null;
+ };
+ items: {}[];
+ menu: {
+ day: number;
+ }[];
+ totals: {
+ cost: number;
+ };
+ };
+}
+
+/** [email protected] (owner: food) */
+/**
+ * food.plan-create-request@1: POST /api/v1/plans on OpenVibe.Food. people 1-12, days 1-14, budget (US dollars for the whole plan) optional, title optional.
+ */
+export interface FoodPlanCreateRequest {
+ people: number;
+ days: number;
+ budget?: number | null;
+ title?: string | null;
+}
+
+/** [email protected] (owner: food) */
+/**
+ * food.plan-result@1: answers of the plan routes on OpenVibe.Food: GET /api/v1/plans → { plans }; GET /api/v1/plans/:id and POST /api/v1/plans (201) → the plan itself (food.plan@1).
+ */
+export type FoodPlanResult =
+ | {
+ plans: FoodPlan[];
+ }
+ | FoodPlan;
new file mode 100644
new file mode 100644
@@ -0,0 +1,21 @@
@@ -0,0 +1,21 @@
+{
+ "id": "food.pantry.read",
+ "version": "1.0",
+ "owner": "food",
+ "status": "active",
+ "visibility": "public",
+ "description": "Read a caller's pantry with the suggestions matched against it: GET /api/v1/pantry → { items, foods, suggestions: { can_make, almost }, note }. The suggestions are deterministic matches against basic recipes, not dietary advice.",
+ "inputSchema": "common.no-body@1",
+ "outputSchema": "food.pantry-result@1",
+ "permissions": [
+ "food:pantry:read"
+ ],
+ "resourceConstraints": [
+ "owner"
+ ],
+ "quotaClass": "food-read",
+ "events": [],
+ "implementedBy": [
+ "GET /api/v1/pantry"
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,22 @@
@@ -0,0 +1,22 @@
+{
+ "id": "food.pantry.write",
+ "version": "1.0",
+ "owner": "food",
+ "status": "active",
+ "visibility": "public",
+ "sensitive": true,
+ "description": "Replace a caller's pantry with exactly this list: PUT /api/v1/pantry with { items: [food slug, …] } (at most 200, each one in the food list; GET /api/v1/foods lists them). Answers like a read.",
+ "inputSchema": "food.pantry-request@1",
+ "outputSchema": "food.pantry-result@1",
+ "permissions": [
+ "food:pantry:write"
+ ],
+ "resourceConstraints": [
+ "owner"
+ ],
+ "quotaClass": "food-write",
+ "events": [],
+ "implementedBy": [
+ "PUT /api/v1/pantry"
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,22 @@
@@ -0,0 +1,22 @@
+{
+ "id": "food.plan.read",
+ "version": "1.0",
+ "owner": "food",
+ "status": "active",
+ "visibility": "public",
+ "description": "Read the plans a caller saved: GET /api/v1/plans (newest first, ?limit to 50) → { plans }, and GET /api/v1/plans/:id → one plan. Only the person who saved a plan (or their tokens) can read it.",
+ "inputSchema": "common.no-body@1",
+ "outputSchema": "food.plan-result@1",
+ "permissions": [
+ "food:plans:read"
+ ],
+ "resourceConstraints": [
+ "owner"
+ ],
+ "quotaClass": "food-read",
+ "events": [],
+ "implementedBy": [
+ "GET /api/v1/plans",
+ "GET /api/v1/plans/:id"
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,22 @@
@@ -0,0 +1,22 @@
+{
+ "id": "food.plan.write",
+ "version": "1.0",
+ "owner": "food",
+ "status": "active",
+ "visibility": "public",
+ "sensitive": true,
+ "description": "Plan a shop for a household and a budget and save it: POST /api/v1/plans with { people, days, budget?, title? }. Food picks budget items from its food list to cover each food group for every person and day, splits them by the cheapest store and lays out a menu; the saved plan (201, Location /api/v1/plans/:id) is the caller's own.",
+ "inputSchema": "food.plan-create-request@1",
+ "outputSchema": "food.plan-result@1",
+ "permissions": [
+ "food:plans:write"
+ ],
+ "resourceConstraints": [
+ "owner"
+ ],
+ "quotaClass": "food-write",
+ "events": [],
+ "implementedBy": [
+ "POST /api/v1/plans"
+ ]
+}
@@ -27,18 +27,6 @@
@@ -27,18 +27,6 @@
"keywords": "openvibe food, compare food delivery prices, meal kits, meal planning, recipe ideas, grocery tools",
"keywords": "openvibe food, compare food delivery prices, meal kits, meal planning, recipe ideas, grocery tools",
"launch": "the food tools moving over from OpenVibe.Tools",
"launch": "the food tools moving over from OpenVibe.Tools",
"relationships": {
"relationships": {
- "noRepo": true,
- "meanwhile": [
- {
- "label": "Food tools",
- "url": "https://food.openvibe.tools",
- "note": "The food and grocery tools work today."
- },
- {
- "label": "OpenVibe.Coupons",
- "url": "https://openvibe.coupons",
- "note": "Coupons are the sibling room."
- }
- ]
+ "service": "food"
}
}
}
}
@@ -27,18 +27,6 @@
@@ -27,18 +27,6 @@
"keywords": "openvibe help, openvibe support, help center, support tickets, troubleshooting",
"keywords": "openvibe help, openvibe support, help center, support tickets, troubleshooting",
"launch": "the ticket service and the Frame's Help button",
"launch": "the ticket service and the Frame's Help button",
"relationships": {
"relationships": {
- "noRepo": true,
- "meanwhile": [
- {
- "label": "Network status",
- "url": "https://openvibe.network/status",
- "note": "Service health and incidents today."
- },
- {
- "label": "OpenVibe.Wiki",
- "url": "https://openvibe.wiki",
- "note": "Guides and concepts."
- }
- ]
+ "service": "help"
}
}
}
}
@@ -27,13 +27,6 @@
@@ -27,13 +27,6 @@
"keywords": "openvibe work, job search, job listings, job alerts, ai job application assistant",
"keywords": "openvibe work, job search, job listings, job alerts, ai job application assistant",
"launch": "job listings and alerts",
"launch": "job listings and alerts",
"relationships": {
"relationships": {
- "noRepo": true,
- "meanwhile": [
- {
- "label": "OpenVibe.Network",
- "url": "https://openvibe.network",
- "note": "One free account for every OpenVibe site, ready today."
- }
- ]
+ "service": "work"
}
}
}
}
new file mode 100644
new file mode 100644
@@ -0,0 +1,84 @@
@@ -0,0 +1,84 @@
+{
+ "version": "0.1.0",
+ "contractRanges": {
+ "openvibe-contracts": ">=0.116.0 <1.0.0"
+ },
+ "id": "food",
+ "name": "OpenVibe.Food",
+ "status": "alpha",
+ "repository": "OpenVibers/OpenVibe.Food",
+ "domains": [
+ "openvibe.food"
+ ],
+ "publicOrigin": "https://openvibe.food",
+ "exposure": {
+ "state": "live",
+ "publicSite": "service"
+ },
+ "capabilities": [
+ "food.plan.read",
+ "food.plan.write",
+ "food.pantry.read",
+ "food.pantry.write"
+ ],
+ "eventsProduced": [],
+ "eventsConsumed": [],
+ "namespacesOwned": [
+ "food.*"
+ ],
+ "lifecycle": {
+ "liveness": {
+ "endpoint": "/api/health",
+ "means": "the process is up and answers HTTP; it checks nothing else (the database is what ready checks)"
+ },
+ "shutdown": {
+ "signal": "SIGTERM",
+ "deadlineSeconds": 10,
+ "drains": [
+ "HTTP server closed",
+ "Network key refresher stopped",
+ "store closed"
+ ]
+ },
+ "startupRecovery": {
+ "resumes": [
+ {
+ "kind": "sessions",
+ "what": "sign-in sessions are rows in the database, so a person stays signed in across the restart; nothing runs in the background that a restart could leave half done"
+ },
+ {
+ "kind": "state",
+ "what": "the OpenStreetMap answer cache is read from food_geo_cache, so a place searched before the stop is not fetched again"
+ }
+ ]
+ },
+ "rollback": {
+ "conditions": [
+ "automatic: ovhost deploy puts the previous sha back and restarts again when the ready URL does not answer 2xx within ready.timeoutSeconds after the restart (exit 3)",
+ "manual: ovhost rollback <service> --to <sha>"
+ ],
+ "window": "automatic only while ovhost waits for readiness after the restart; manual afterwards, as long as no blocker applies",
+ "blockers": {
+ "none": "migrations 0001-0002 only create Food's own tables; an earlier release has none to read"
+ }
+ },
+ "contracts": {
+ "range": ">=0.116.0 <1.0.0"
+ },
+ "leases": {
+ "none": "no claims: one process serves the site"
+ }
+ },
+ "notes": "Port 4970, unit openvibe-food.service. Food near you (food banks and budget grocers from OpenStreetMap through Nominatim and Overpass, one request a second per host, cached 7 days, ODbL attribution on every answer), the budget food list carried over from OpenVibe.Tools with chain-by-chain prices, meal plans for a household and a budget (saved per person), and a pantry with deterministic \"what can I make\" suggestions. Public reads need no token; plans and pantries are a signed-in person's (or an app token's with the capability). Consumes OpenVibe.Network (SSO with PKCE S256 as OAuth client food, JWKS). Owns food_plans, food_pantry and food_geo_cache.",
+ "health": "/api/health",
+ "ready": "/api/ready",
+ "internalOrigin": "http://127.0.0.1:4970",
+ "site": {
+ "name": "Food",
+ "icon": "food",
+ "tagline": "What should we eat?",
+ "what": "Find food banks and budget grocers near you, plan a week of meals for your household and budget, and see what your cupboard can make.",
+ "legalProfile": "tools",
+ "position": 24
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,75 @@
@@ -0,0 +1,75 @@
+{
+ "version": "0.1.0",
+ "contractRanges": {
+ "openvibe-contracts": ">=0.116.0 <1.0.0"
+ },
+ "id": "help",
+ "name": "OpenVibe.Help",
+ "status": "alpha",
+ "repository": "OpenVibers/OpenVibe.Help",
+ "domains": [
+ "openvibe.help"
+ ],
+ "publicOrigin": "https://openvibe.help",
+ "exposure": {
+ "state": "live",
+ "publicSite": "service"
+ },
+ "capabilities": [],
+ "eventsProduced": [],
+ "eventsConsumed": [],
+ "namespacesOwned": [
+ "help.*"
+ ],
+ "lifecycle": {
+ "liveness": {
+ "endpoint": "/api/health",
+ "means": "the process is up and answers HTTP; it checks nothing else (the database is what ready checks)"
+ },
+ "shutdown": {
+ "signal": "SIGTERM",
+ "deadlineSeconds": 10,
+ "drains": [
+ "HTTP server closed",
+ "Network key refresher stopped",
+ "store closed"
+ ]
+ },
+ "startupRecovery": {
+ "resumes": [
+ {
+ "kind": "sessions",
+ "what": "sign-in sessions are rows in the database, so a person stays signed in across the restart; nothing runs in the background that a restart could leave half done"
+ }
+ ]
+ },
+ "rollback": {
+ "conditions": [
+ "automatic: ovhost deploy puts the previous sha back and restarts again when the ready URL does not answer 2xx within ready.timeoutSeconds after the restart (exit 3)",
+ "manual: ovhost rollback <service> --to <sha>"
+ ],
+ "window": "automatic only while ovhost waits for readiness after the restart; manual afterwards, as long as no blocker applies",
+ "blockers": {
+ "none": "migrations 0001-0002 only create Help's own tables; an earlier release has none to read"
+ }
+ },
+ "contracts": {
+ "range": ">=0.116.0 <1.0.0"
+ },
+ "leases": {
+ "none": "no claims: one process serves the site"
+ }
+ },
+ "notes": "Port 5020, unit openvibe-help.service. The network's help centre: a catalog of every OpenVibe product site built from openvibe-contracts (each site's state, one line, pillars and FAQ), articles generated from it, search across them, and support tickets a signed-in person opens and staff answer (staff judged by the Network role claim). Consumes OpenVibe.Network (SSO with PKCE S256 as OAuth client help, JWKS). Owns help_tickets and help_messages. No capabilities yet: tickets are a person's own, not an app's.",
+ "health": "/api/health",
+ "ready": "/api/ready",
+ "internalOrigin": "http://127.0.0.1:5020",
+ "site": {
+ "name": "Help",
+ "icon": "help",
+ "tagline": "Answers for every OpenVibe site",
+ "what": "Every OpenVibe site in one help centre: what each one does, its common questions, and a ticket to a person when the answer is not there.",
+ "legalProfile": "account",
+ "position": 25
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,76 @@
@@ -0,0 +1,76 @@
+{
+ "version": "0.1.0",
+ "contractRanges": {
+ "openvibe-contracts": ">=0.116.0 <1.0.0"
+ },
+ "id": "work",
+ "name": "OpenVibe.Work",
+ "status": "alpha",
+ "repository": "OpenVibers/OpenVibe.Work",
+ "domains": [
+ "openvibe.work"
+ ],
+ "publicOrigin": "https://openvibe.work",
+ "exposure": {
+ "state": "live",
+ "publicSite": "service"
+ },
+ "capabilities": [],
+ "eventsProduced": [],
+ "eventsConsumed": [],
+ "namespacesOwned": [
+ "work.*"
+ ],
+ "lifecycle": {
+ "liveness": {
+ "endpoint": "/api/health",
+ "means": "the process is up and answers HTTP; it checks nothing else (the database is what ready checks)"
+ },
+ "shutdown": {
+ "signal": "SIGTERM",
+ "deadlineSeconds": 10,
+ "drains": [
+ "HTTP server closed",
+ "ingest timer stopped (a fetch in flight is abandoned; its rows are written per listing)",
+ "Network key refresher stopped",
+ "store closed"
+ ]
+ },
+ "startupRecovery": {
+ "resumes": [
+ {
+ "kind": "jobs",
+ "what": "the ingest starts again on its timer; each source is fetched when its last fetch is older than 6 hours, so a fetch cut short by a restart is simply redone"
+ }
+ ]
+ },
+ "rollback": {
+ "conditions": [
+ "automatic: ovhost deploy puts the previous sha back and restarts again when the ready URL does not answer 2xx within ready.timeoutSeconds after the restart (exit 3)",
+ "manual: ovhost rollback <service> --to <sha>"
+ ],
+ "window": "automatic only while ovhost waits for readiness after the restart; manual afterwards, as long as no blocker applies",
+ "blockers": {
+ "none": "migrations 0001-0002 only create Work's own tables; an earlier release has none to read"
+ }
+ },
+ "contracts": {
+ "range": ">=0.116.0 <1.0.0"
+ },
+ "leases": {
+ "none": "no claims: one process serves the site"
+ }
+ },
+ "notes": "Port 4960, unit openvibe-work.service. Job listings gathered from open job-board APIs (Arbeitnow, Remotive, RemoteOK) with their provenance: a short plain-text excerpt only, the source named and the original listing linked on every list and page, never the full description. Search with remote/type/location filters, JobPosting JSON-LD, and saved searches with a count of what is new since you last looked. The ingest runs every 6 hours per source when WORK_INGEST=on, upserts by (source, source_id) and expires listings not seen for 30 days. Consumes OpenVibe.Network (SSO with PKCE S256 as OAuth client work, JWKS). Owns work_listings, work_source_fetches and work_saved_searches. No capabilities yet: saved searches are a person's, listings are public.",
+ "health": "/api/health",
+ "ready": "/api/ready",
+ "internalOrigin": "http://127.0.0.1:4960",
+ "site": {
+ "name": "Work",
+ "icon": "work",
+ "tagline": "Find work, with an agent on your side",
+ "what": "Job listings from open job boards in one search, each linked back to where you apply, with saved searches that tell you what is new.",
+ "legalProfile": "info",
+ "position": 26
+ }
+}
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
{
{
"name": "openvibe-contracts",
"name": "openvibe-contracts",
- "version": "0.115.0",
+ "version": "0.116.0",
"description": "Machine-readable contracts for the OpenVibe network: schemas, manifests, ids, error envelope, capability checks.",
"description": "Machine-readable contracts for the OpenVibe network: schemas, manifests, ids, error envelope, capability checks.",
"main": "index.js",
"main": "index.js",
"types": "generated/typescript/index.d.ts",
"types": "generated/typescript/index.d.ts",