AiOffer ai.offer@1
Generated at from
openvibe-contracts v0.419.0 and
openvibe-sdk v0.161.0.
- Version
- 1.0.0
- Owner
- ai
- Visibility
- first-party
- Status
- active
- Compatibility
- backward
- Schema
https://openvibe.network/contracts/ai/offer.v1.json
ai.offer@1: one community AI offer, a person's machine (OpenVibe Link's ai plugin, "Offer to the network") serving its local models to requests that opt in to community capacity. OpenVibe.Bot reports these fields (POST /internal/ai/offers/report on Network, network.resource.report; a report with any other field is refused whole), and OpenVibe.AI reads them from Network (GET /internal/ai/offers, network.registry.read, { offers, generated_at, count }). Network adds `pool` (ai.offer-pool@1) to the head's offer when the machine is the head of an approved pool (bot.machine-pool@1), and leaves a pool member's own offer out of the list while it is in a pool. For a pool the hours, audience, price, readiness, idle-only and verification apply to the whole pool: it is ready only while every member has approved, renewed its readiness in the last few seconds, and is still the owner's. Nothing here is public: AI shows Exchange a hashed offer id and no owner or machine id.
Fields
| Field | Type | Required | Description | Constraints |
|---|---|---|---|---|
offer_id | string | yes | `ai:<machine_id>`: one offer per machine. |
|
machine_id | string | yes | The machine that answers (for a pool, its head). |
|
owner | string | yes | The machine's owner, who earns the offer's credits (a self-served run earns none). |
|
models | array of string | yes | The models offered, as the machine's model server names them. |
|
hours | object | yes | The local-time window the offer is open; start equal to end is all day, and an overnight window wraps. |
|
hours.start | string | yes |
| |
hours.end | string | yes |
| |
hours.tz | string | yes | An IANA time zone, such as Europe/Paris, or UTC. |
|
concurrency | integer | yes | How many requests may run at once; another is refused as busy, never queued. |
|
price | object | yes | Credits per million input and output tokens; 0 and 0 is a volunteer, who earns kudos. One price for a pool. |
|
price.in_per_million | number | yes |
| |
price.out_per_million | number | yes |
| |
audience | enum | yes | Who may use it: everyone, the owner's followers, or one group (`group`). |
|
group | string | null | yes | The group's id when the audience is `group`, otherwise null. |
|
ready | boolean | yes | Whether the machine (for a pool, every member) can answer now: online, inside its hours, not paused by idle-only or a kill switch. | |
health | object | yes |
| |
health.status | enum | yes |
| |
health.checked_at | string |
| ||
pool | ai.offer-pool | Present when the offer is served by a pool of the owner's machines (ai.offer-pool@1). |
Examples
From the contract's own test fixtures: valid ones validate, rejected ones must fail.
Valid: group-audience
{
"offer_id": "ai:dev_01J8Z3Q4R5S6T7V8W9X0Y1Z2D1",
"machine_id": "dev_01J8Z3Q4R5S6T7V8W9X0Y1Z2D1",
"owner": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A1",
"models": [
"qwen3:8b"
],
"hours": {
"start": "22:00",
"end": "08:00",
"tz": "Europe/Paris"
},
"concurrency": 1,
"price": {
"in_per_million": 0.2,
"out_per_million": 0.6
},
"audience": "group",
"group": "grp_friends",
"ready": true,
"health": {
"status": "up",
"checked_at": "2026-11-07T10:00:00.000Z"
}
}Valid: pool-waiting-for-approvals
{
"offer_id": "ai:dev_01J8Z3Q4R5S6T7V8W9X0Y1Z2D1",
"machine_id": "dev_01J8Z3Q4R5S6T7V8W9X0Y1Z2D1",
"owner": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A1",
"models": [
"llama-3.3-70b-instruct-q4_k_m"
],
"hours": {
"start": "22:00",
"end": "08:00",
"tz": "Europe/Paris"
},
"concurrency": 1,
"price": {
"in_per_million": 0.2,
"out_per_million": 0.6
},
"audience": "everyone",
"group": null,
"ready": false,
"health": {
"status": "down"
},
"pool": {
"machines": [
"dev_01J8Z3Q4R5S6T7V8W9X0Y1Z2D1",
"dev_01J8Z3Q4R5S6T7V8W9X0Y1Z2D2"
],
"head": "dev_01J8Z3Q4R5S6T7V8W9X0Y1Z2D1"
}
}Valid: pool
{
"offer_id": "ai:dev_01J8Z3Q4R5S6T7V8W9X0Y1Z2D1",
"machine_id": "dev_01J8Z3Q4R5S6T7V8W9X0Y1Z2D1",
"owner": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A1",
"models": [
"llama-3.3-70b-instruct-q4_k_m"
],
"hours": {
"start": "22:00",
"end": "08:00",
"tz": "Europe/Paris"
},
"concurrency": 1,
"price": {
"in_per_million": 0.2,
"out_per_million": 0.6
},
"audience": "everyone",
"group": null,
"ready": true,
"health": {
"status": "up",
"checked_at": "2026-11-07T10:00:00.000Z"
},
"pool": {
"machines": [
"dev_01J8Z3Q4R5S6T7V8W9X0Y1Z2D1",
"dev_01J8Z3Q4R5S6T7V8W9X0Y1Z2D2"
],
"head": "dev_01J8Z3Q4R5S6T7V8W9X0Y1Z2D1"
}
}Valid: single-machine
{
"offer_id": "ai:dev_01J8Z3Q4R5S6T7V8W9X0Y1Z2D1",
"machine_id": "dev_01J8Z3Q4R5S6T7V8W9X0Y1Z2D1",
"owner": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A1",
"models": [
"qwen3:8b"
],
"hours": {
"start": "22:00",
"end": "08:00",
"tz": "Europe/Paris"
},
"concurrency": 1,
"price": {
"in_per_million": 0.2,
"out_per_million": 0.6
},
"audience": "everyone",
"group": null,
"ready": true,
"health": {
"status": "up",
"checked_at": "2026-11-07T10:00:00.000Z"
}
}Rejected: extra-field
{
"offer_id": "ai:dev_01J8Z3Q4R5S6T7V8W9X0Y1Z2D1",
"machine_id": "dev_01J8Z3Q4R5S6T7V8W9X0Y1Z2D1",
"owner": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A1",
"models": [
"llama-3.3-70b-instruct-q4_k_m"
],
"hours": {
"start": "22:00",
"end": "08:00",
"tz": "Europe/Paris"
},
"concurrency": 1,
"price": {
"in_per_million": 0.2,
"out_per_million": 0.6
},
"audience": "everyone",
"group": null,
"ready": true,
"health": {
"status": "up",
"checked_at": "2026-11-07T10:00:00.000Z"
},
"pool": {
"machines": [
"dev_01J8Z3Q4R5S6T7V8W9X0Y1Z2D1",
"dev_01J8Z3Q4R5S6T7V8W9X0Y1Z2D2"
],
"head": "dev_01J8Z3Q4R5S6T7V8W9X0Y1Z2D1"
},
"private_key": "not-permitted"
}Rejected: missing-owner
{
"offer_id": "ai:dev_01J8Z3Q4R5S6T7V8W9X0Y1Z2D1",
"machine_id": "dev_01J8Z3Q4R5S6T7V8W9X0Y1Z2D1",
"models": [
"llama-3.3-70b-instruct-q4_k_m"
],
"hours": {
"start": "22:00",
"end": "08:00",
"tz": "Europe/Paris"
},
"concurrency": 1,
"price": {
"in_per_million": 0.2,
"out_per_million": 0.6
},
"audience": "everyone",
"group": null,
"ready": true,
"health": {
"status": "up",
"checked_at": "2026-11-07T10:00:00.000Z"
},
"pool": {
"machines": [
"dev_01J8Z3Q4R5S6T7V8W9X0Y1Z2D1",
"dev_01J8Z3Q4R5S6T7V8W9X0Y1Z2D2"
],
"head": "dev_01J8Z3Q4R5S6T7V8W9X0Y1Z2D1"
}
}Rejected: offer-id-not-a-machine
{
"offer_id": "ai:everything",
"machine_id": "dev_01J8Z3Q4R5S6T7V8W9X0Y1Z2D1",
"owner": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A1",
"models": [
"qwen3:8b"
],
"hours": {
"start": "22:00",
"end": "08:00",
"tz": "Europe/Paris"
},
"concurrency": 1,
"price": {
"in_per_million": 0.2,
"out_per_million": 0.6
},
"audience": "everyone",
"group": null,
"ready": true,
"health": {
"status": "up",
"checked_at": "2026-11-07T10:00:00.000Z"
}
}Rejected: one-machine-pool
{
"offer_id": "ai:dev_01J8Z3Q4R5S6T7V8W9X0Y1Z2D1",
"machine_id": "dev_01J8Z3Q4R5S6T7V8W9X0Y1Z2D1",
"owner": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A1",
"models": [
"llama-3.3-70b-instruct-q4_k_m"
],
"hours": {
"start": "22:00",
"end": "08:00",
"tz": "Europe/Paris"
},
"concurrency": 1,
"price": {
"in_per_million": 0.2,
"out_per_million": 0.6
},
"audience": "everyone",
"group": null,
"ready": true,
"health": {
"status": "up",
"checked_at": "2026-11-07T10:00:00.000Z"
},
"pool": {
"machines": [
"dev_01J8Z3Q4R5S6T7V8W9X0Y1Z2D1"
],
"head": "dev_01J8Z3Q4R5S6T7V8W9X0Y1Z2D1"
}
}Rejected: pool-with-address
{
"offer_id": "ai:dev_01J8Z3Q4R5S6T7V8W9X0Y1Z2D1",
"machine_id": "dev_01J8Z3Q4R5S6T7V8W9X0Y1Z2D1",
"owner": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A1",
"models": [
"llama-3.3-70b-instruct-q4_k_m"
],
"hours": {
"start": "22:00",
"end": "08:00",
"tz": "Europe/Paris"
},
"concurrency": 1,
"price": {
"in_per_million": 0.2,
"out_per_million": 0.6
},
"audience": "everyone",
"group": null,
"ready": true,
"health": {
"status": "up",
"checked_at": "2026-11-07T10:00:00.000Z"
},
"pool": {
"machines": [
"dev_01J8Z3Q4R5S6T7V8W9X0Y1Z2D1",
"dev_01J8Z3Q4R5S6T7V8W9X0Y1Z2D2"
],
"head": "dev_01J8Z3Q4R5S6T7V8W9X0Y1Z2D1",
"address": "192.168.1.20/24"
}
}Validate
const contracts = require('openvibe-contracts');
contracts.validate('ai.offer@1', value); // { valid, errors: [{ path, message }] }