openvibers/openvibe.contracts Commits
v0.14.0: mods.mod-manifest@1 (ADR-013); OpenVibe.Games capabilities games.mod.manage|read, games.world.announce, games.prop.place; Network plans mods.grant.manage
Changes
@@@ -147,6 +147,16 @@"compatibility": "backward","status": "active","adr": "ADR-017"++ },++ {++ "id": "mods.mod-manifest",++ "version": "1.0.0",++ "owner": "contracts",++ "schema": "mods/mod-manifest.v1.json",++ "visibility": "public",++ "compatibility": "backward",++ "status": "active",++ "adr": "ADR-013"}]}nnew file mode 100644@@@ -0,0 +1,134 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/mods/mod-manifest.v1.json",++ "title": "ModManifest",++ "description": "A mod as the platform knows it (ADR-013): who publishes it, which runtime runs it, which capabilities it asks for and the resources it may use. Requested capabilities are only a request: the install's approved subset is the grant, and trust tiers are install metadata that never change a grant check. The runtime-specific payload (a data pack, a script bundle) is not part of the manifest.",++ "type": "object",++ "required": [++ "id",++ "name",++ "version",++ "publisher",++ "target",++ "runtime",++ "permissions",++ "resources",++ "compatibility"++ ],++ "additionalProperties": false,++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^mod_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "Stable mod id; its principal subject is mod:<id>."++ },++ "name": { "type": "string", "minLength": 1, "maxLength": 80 },++ "version": {++ "type": "string",++ "pattern": "^(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(-[0-9A-Za-z.-]+)?$",++ "description": "Semantic version of this release."++ },++ "description": { "type": "string", "maxLength": 1000 },++ "publisher": {++ "$ref": "../identity/subject-ref.v1.json",++ "description": "Who publishes the mod (a user or an app subject)."++ },++ "target": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9-]{1,39}\\.[a-z][a-z0-9-]{1,39}$",++ "description": "Where the mod runs: <service>.<surface>, e.g. games.browser, games.source, live.overlay."++ },++ "runtime": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9-]{1,39}@[1-9][0-9]{0,3}$",++ "description": "Runtime adapter and its major version, e.g. games-content@1 (declarative data pack) or source-quickjs@1."++ },++ "permissions": {++ "type": "object",++ "required": ["capabilities"],++ "additionalProperties": false,++ "properties": {++ "capabilities": {++ "description": "Capability ids the mod asks for (3+ segments). The target runtime binds only those it implements, and only once granted.",++ "type": "array",++ "maxItems": 64,++ "uniqueItems": true,++ "items": { "type": "string", "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+){2,}$" }++ },++ "events": {++ "description": "Event types the mod wants delivered to it.",++ "type": "array",++ "maxItems": 64,++ "uniqueItems": true,++ "items": { "type": "string", "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+){2,}$" }++ },++ "modules": {++ "description": "User-module namespaces the mod wants to read or write (a trailing .* names a family).",++ "type": "array",++ "maxItems": 32,++ "uniqueItems": true,++ "items": { "type": "string", "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)*(\\.\\*)?$" }++ },++ "mediaNamespaces": {++ "description": "Media namespaces the mod wants to read or write.",++ "type": "array",++ "maxItems": 32,++ "uniqueItems": true,++ "items": { "type": "string", "pattern": "^[a-z][a-z0-9_-]*(\\.[a-z0-9_-]+)*$" }++ }++ }++ },++ "resources": {++ "type": "object",++ "required": ["cpuMs", "memoryMb", "storageMb"],++ "additionalProperties": false,++ "description": "The budget the mod asks for. Runtimes meter it; the sandbox that enforces it lives in OpenVibe.Host (Stage C).",++ "properties": {++ "cpuMs": {++ "type": "number",++ "minimum": 0,++ "maximum": 1000,++ "description": "CPU milliseconds per tick (game runtimes) or per request."++ },++ "memoryMb": { "type": "integer", "minimum": 0, "maximum": 4096 },++ "storageMb": { "type": "integer", "minimum": 0, "maximum": 102400 },++ "outboundHosts": {++ "description": "Hosts the mod may reach over the network. Empty or absent = none.",++ "type": "array",++ "maxItems": 32,++ "uniqueItems": true,++ "items": {++ "type": "string",++ "pattern": "^(?=.{1,253}$)([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z]{2,63}$"++ }++ }++ }++ },++ "assets": {++ "description": "Assets the mod ships, as Media object references.",++ "type": "array",++ "maxItems": 256,++ "items": { "$ref": "../media/media-ref.v1.json" }++ },++ "compatibility": {++ "type": "object",++ "required": ["runtime"],++ "additionalProperties": false,++ "properties": {++ "runtime": {++ "type": "string",++ "minLength": 1,++ "maxLength": 100,++ "description": "Semver range of the runtime this release works with, e.g. \">=1.0.0 <2.0.0\"."++ },++ "contracts": {++ "type": "string",++ "minLength": 1,++ "maxLength": 100,++ "description": "Semver range of openvibe-contracts releases it was built against."++ }++ }++ },++ "homepage": { "type": "string", "pattern": "^https://[^\\s]{1,2000}$" }++ }++}nnew file mode 100644@@@ -0,0 +1 @@++{ "id": "mod_01JABCDEFGHJKMNPQRSTVWXYZ0", "name": "X", "version": "1.0.0", "publisher": { "type": "user", "id": "usr_01JABCDEFGHJKMNPQRSTVWXYZ0" }, "target": "games.browser", "runtime": "games-content@1", "resources": { "cpuMs": 1, "memoryMb": 0, "storageMb": 0 }, "compatibility": { "runtime": ">=1.0.0" } }nnew file mode 100644@@@ -0,0 +1,12 @@++{++ "id": "mod_01JABCDEFGHJKMNPQRSTVWXYZ0",++ "name": "Town Square",++ "version": "1.0.0",++ "description": "A public workbench and a greeting.",++ "publisher": { "type": "user", "id": "usr_01JABCDEFGHJKMNPQRSTVWXYZ0" },++ "target": "games.browser",++ "runtime": "games-content@1",++ "permissions": { "capabilities": ["games.world.announce", "games.prop.place"] },++ "resources": { "cpuMs": 1, "memoryMb": 0, "storageMb": 0 },++ "compatibility": { "runtime": ">=1.0.0 <2.0.0" }++}@@@ -1620,6 +1620,185 @@}}}++ },++ "mods.mod-manifest": {++ "version": "1.0.0",++ "owner": "contracts",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/mods/mod-manifest.v1.json",++ "title": "ModManifest",++ "description": "A mod as the platform knows it (ADR-013): who publishes it, which runtime runs it, which capabilities it asks for and the resources it may use. Requested capabilities are only a request: the install's approved subset is the grant, and trust tiers are install metadata that never change a grant check. The runtime-specific payload (a data pack, a script bundle) is not part of the manifest.",++ "type": "object",++ "required": [++ "id",++ "name",++ "version",++ "publisher",++ "target",++ "runtime",++ "permissions",++ "resources",++ "compatibility"++ ],++ "additionalProperties": false,++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^mod_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "Stable mod id; its principal subject is mod:<id>."++ },++ "name": {++ "type": "string",++ "minLength": 1,++ "maxLength": 80++ },++ "version": {++ "type": "string",++ "pattern": "^(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(-[0-9A-Za-z.-]+)?$",++ "description": "Semantic version of this release."++ },++ "description": {++ "type": "string",++ "maxLength": 1000++ },++ "publisher": {++ "$ref": "../identity/subject-ref.v1.json",++ "description": "Who publishes the mod (a user or an app subject)."++ },++ "target": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9-]{1,39}\\.[a-z][a-z0-9-]{1,39}$",++ "description": "Where the mod runs: <service>.<surface>, e.g. games.browser, games.source, live.overlay."++ },++ "runtime": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9-]{1,39}@[1-9][0-9]{0,3}$",++ "description": "Runtime adapter and its major version, e.g. games-content@1 (declarative data pack) or source-quickjs@1."++ },++ "permissions": {++ "type": "object",++ "required": [++ "capabilities"++ ],++ "additionalProperties": false,++ "properties": {++ "capabilities": {++ "description": "Capability ids the mod asks for (3+ segments). The target runtime binds only those it implements, and only once granted.",++ "type": "array",++ "maxItems": 64,++ "uniqueItems": true,++ "items": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+){2,}$"++ }++ },++ "events": {++ "description": "Event types the mod wants delivered to it.",++ "type": "array",++ "maxItems": 64,++ "uniqueItems": true,++ "items": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+){2,}$"++ }++ },++ "modules": {++ "description": "User-module namespaces the mod wants to read or write (a trailing .* names a family).",++ "type": "array",++ "maxItems": 32,++ "uniqueItems": true,++ "items": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)*(\\.\\*)?$"++ }++ },++ "mediaNamespaces": {++ "description": "Media namespaces the mod wants to read or write.",++ "type": "array",++ "maxItems": 32,++ "uniqueItems": true,++ "items": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9_-]*(\\.[a-z0-9_-]+)*$"++ }++ }++ }++ },++ "resources": {++ "type": "object",++ "required": [++ "cpuMs",++ "memoryMb",++ "storageMb"++ ],++ "additionalProperties": false,++ "description": "The budget the mod asks for. Runtimes meter it; the sandbox that enforces it lives in OpenVibe.Host (Stage C).",++ "properties": {++ "cpuMs": {++ "type": "number",++ "minimum": 0,++ "maximum": 1000,++ "description": "CPU milliseconds per tick (game runtimes) or per request."++ },++ "memoryMb": {++ "type": "integer",++ "minimum": 0,++ "maximum": 4096++ },++ "storageMb": {++ "type": "integer",++ "minimum": 0,++ "maximum": 102400++ },++ "outboundHosts": {++ "description": "Hosts the mod may reach over the network. Empty or absent = none.",++ "type": "array",++ "maxItems": 32,++ "uniqueItems": true,++ "items": {++ "type": "string",++ "pattern": "^(?=.{1,253}$)([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z]{2,63}$"++ }++ }++ }++ },++ "assets": {++ "description": "Assets the mod ships, as Media object references.",++ "type": "array",++ "maxItems": 256,++ "items": {++ "$ref": "../media/media-ref.v1.json"++ }++ },++ "compatibility": {++ "type": "object",++ "required": [++ "runtime"++ ],++ "additionalProperties": false,++ "properties": {++ "runtime": {++ "type": "string",++ "minLength": 1,++ "maxLength": 100,++ "description": "Semver range of the runtime this release works with, e.g. \">=1.0.0 <2.0.0\"."++ },++ "contracts": {++ "type": "string",++ "minLength": 1,++ "maxLength": 100,++ "description": "Semver range of openvibe-contracts releases it was built against."++ }++ }++ },++ "homepage": {++ "type": "string",++ "pattern": "^https://[^\\s]{1,2000}$"++ }++ }++ }}}}@@@ -3198,3 +3198,89 @@ export interface SourceItem {change_seq?: number;revisions?: unknown[];}++++/** [email protected] (owner: contracts) */++/**++ * A mod as the platform knows it (ADR-013): who publishes it, which runtime runs it, which capabilities it asks for and the resources it may use. Requested capabilities are only a request: the install's approved subset is the grant, and trust tiers are install metadata that never change a grant check. The runtime-specific payload (a data pack, a script bundle) is not part of the manifest.++ */++export interface ModManifest {++ /**++ * Stable mod id; its principal subject is mod:<id>.++ */++ id: string;++ name: string;++ /**++ * Semantic version of this release.++ */++ version: string;++ description?: string;++ publisher: SubjectRef;++ /**++ * Where the mod runs: <service>.<surface>, e.g. games.browser, games.source, live.overlay.++ */++ target: string;++ /**++ * Runtime adapter and its major version, e.g. games-content@1 (declarative data pack) or source-quickjs@1.++ */++ runtime: string;++ permissions: {++ /**++ * Capability ids the mod asks for (3+ segments). The target runtime binds only those it implements, and only once granted.++ *++ * @maxItems 64++ */++ capabilities: string[];++ /**++ * Event types the mod wants delivered to it.++ *++ * @maxItems 64++ */++ events?: string[];++ /**++ * User-module namespaces the mod wants to read or write (a trailing .* names a family).++ *++ * @maxItems 32++ */++ modules?: string[];++ /**++ * Media namespaces the mod wants to read or write.++ *++ * @maxItems 32++ */++ mediaNamespaces?: string[];++ };++ /**++ * The budget the mod asks for. Runtimes meter it; the sandbox that enforces it lives in OpenVibe.Host (Stage C).++ */++ resources: {++ /**++ * CPU milliseconds per tick (game runtimes) or per request.++ */++ cpuMs: number;++ memoryMb: number;++ storageMb: number;++ /**++ * Hosts the mod may reach over the network. Empty or absent = none.++ *++ * @maxItems 32++ */++ outboundHosts?: string[];++ };++ /**++ * Assets the mod ships, as Media object references.++ *++ * @maxItems 256++ */++ assets?: MediaRef[];++ compatibility: {++ /**++ * Semver range of the runtime this release works with, e.g. ">=1.0.0 <2.0.0".++ */++ runtime: string;++ /**++ * Semver range of openvibe-contracts releases it was built against.++ */++ contracts?: string;++ };++ homepage?: string;++}nnew file mode 100644@@@ -0,0 +1,28 @@++{++ "id": "games.mod.manage",++ "version": "1.0",++ "owner": "games",++ "status": "active",++ "visibility": "first-party",++ "description": "Manage mod installs in the browser game's registry (ADR-013): install a mods/mod-manifest.v1 manifest with its games-content@1 pack, approve or revoke individual capabilities of an install, enable, disable, and revoke an install for good (its effects end on the next server tick). Every action is audited and published as a games.mod.* event. Trust tiers are metadata and never grant anything. Also accepted from an openvibe.network owner/admin session.",++ "inputSchema": "mods.mod-manifest@1",++ "permissions": ["games:mods:admin"],++ "resourceConstraints": ["none"],++ "quotaClass": "games-admin",++ "events": [++ "games.mod.installed",++ "games.mod.enabled",++ "games.mod.disabled",++ "games.mod.grants_changed",++ "games.mod.revoked"++ ],++ "implementedBy": [++ "POST /api/v1/mods",++ "POST /api/v1/mods/:id/enable",++ "POST /api/v1/mods/:id/disable",++ "POST /api/v1/mods/:id/revoke",++ "POST /api/v1/mods/:id/grants",++ "DELETE /api/v1/mods/:id/grants/:capability",++ "GET /api/v1/mods/:id/audit"++ ]++}nnew file mode 100644@@@ -0,0 +1,14 @@++{++ "id": "games.mod.read",++ "version": "1.0",++ "owner": "games",++ "status": "active",++ "visibility": "public",++ "description": "Read the browser game's installed mods: manifest, content pack, status, trust tier, requested and granted capabilities. Public; no token needed.",++ "outputSchema": "mods.mod-manifest@1",++ "permissions": ["games:mods:read"],++ "resourceConstraints": ["none"],++ "quotaClass": "games-read",++ "events": [],++ "implementedBy": ["GET /api/v1/mods", "GET /api/v1/mods/:id"]++}nnew file mode 100644@@@ -0,0 +1,15 @@++{++ "id": "games.prop.place",++ "version": "1.0",++ "owner": "games",++ "status": "active",++ "visibility": "public",++ "description": "Runtime binding for mods (games-content@1): place and remove inert props (existing content items without health, storage, shop, machine or vehicle-part capabilities) in the world, owned by the mod so players cannot pick them up or move them. The props exist only while the capability is granted and the install enabled: revoking either despawns them on the next tick.",++ "permissions": ["games:world:place"],++ "resourceConstraints": ["none"],++ "quotaClass": "games-mod-runtime",++ "events": [],++ "implementedBy": [++ "mod runtime binding ModApi.placeProp / ModApi.removeProp (apps/server/src/mods/runtime.ts)"++ ]++}nnew file mode 100644@@@ -0,0 +1,13 @@++{++ "id": "games.world.announce",++ "version": "1.0",++ "owner": "games",++ "status": "active",++ "visibility": "public",++ "description": "Runtime binding for mods (games-content@1): broadcast a short server announcement to every player in the world, prefixed with the mod's name, at most once a minute per announcement. Checked at call time against the install's approved capabilities; a revoked grant stops it on the next tick.",++ "permissions": ["games:world:announce"],++ "resourceConstraints": ["none"],++ "quotaClass": "games-mod-runtime",++ "events": [],++ "implementedBy": ["mod runtime binding ModApi.announce (apps/server/src/mods/runtime.ts)"]++}nnew file mode 100644@@@ -0,0 +1,14 @@++{++ "id": "mods.grant.manage",++ "version": "1.0",++ "owner": "network",++ "status": "planned",++ "visibility": "first-party",++ "description": "Planned (ADR-013, grants in Network): create the `mod` principal (mod:mod_<ULID>) for an install and approve or revoke the subset of its manifest's requested capabilities, so a mod's grants are checked like any other principal's. Until Network implements it, each runtime keeps the approved subset itself (OpenVibe.Games: mod_grants).",++ "inputSchema": "mods.mod-manifest@1",++ "permissions": ["network:grants:admin"],++ "resourceConstraints": ["none"],++ "quotaClass": "network-admin",++ "events": [],++ "implementedBy": []++}@@@ -1,22 +1,34 @@{-- "version": "0.1.0",++ "version": "0.2.0","contractRanges": {-- "openvibe-contracts": ">=0.2.0 <1.0.0"++ "openvibe-contracts": ">=0.8.0 <1.0.0"},"id": "games","name": "OpenVibe.Games","status": "stable","repository": "OpenVibers/OpenVibe.Games",-- "domains": [-- "openvibe.games",-- "play.openvibe.games"-- ],++ "domains": ["openvibe.games", "play.openvibe.games"],"publicOrigin": "https://openvibe.games","health": "/healthz",-- "capabilities": [],-- "eventsProduced": [],++ "capabilities": [++ "games.mod.manage",++ "games.mod.read",++ "games.world.announce",++ "games.prop.place"++ ],++ "eventsProduced": [++ "games.player.joined",++ "games.player.left",++ "games.skill.leveled",++ "games.blueprint.unlocked",++ "games.world.saved",++ "games.mod.installed",++ "games.mod.enabled",++ "games.mod.disabled",++ "games.mod.grants_changed",++ "games.mod.revoked"++ ],"eventsConsumed": [],-- "namespacesOwned": [-- "games.*"-- ]++ "namespacesOwned": ["games.*"],++ "notes": "Scraplandia (browser runtime). Players are keyed by canonical Network subjects (ADR-0006 in the repo). Calls Events (events.event.publish), Media (media.object.upload, namespace games: map-editor assets) and Network identity (identity.subject.resolve, batch legacy adoption) with the games client-credentials principal. Mods: games-content@1 declarative packs only; executable mods wait for OpenVibe.Host Stage C. No achievement system exists, so there are no games.achievement.* events."}@@@ -24,7 +24,8 @@"network.coins.transfer","network.notifications.push","network.modules.read",-- "network.modules.write"++ "network.modules.write",++ "mods.grant.manage"],"eventsProduced": [],"eventsConsumed": [],@@@ -1,6 +1,6 @@{"name": "openvibe-contracts",-- "version": "0.13.0",++ "version": "0.14.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",
@@ -147,6 +147,16 @@
@@ -147,6 +147,16 @@
"compatibility": "backward",
"compatibility": "backward",
"status": "active",
"status": "active",
"adr": "ADR-017"
"adr": "ADR-017"
+ },
+ {
+ "id": "mods.mod-manifest",
+ "version": "1.0.0",
+ "owner": "contracts",
+ "schema": "mods/mod-manifest.v1.json",
+ "visibility": "public",
+ "compatibility": "backward",
+ "status": "active",
+ "adr": "ADR-013"
}
}
]
]
}
}
new file mode 100644
new file mode 100644
@@ -0,0 +1,134 @@
@@ -0,0 +1,134 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/mods/mod-manifest.v1.json",
+ "title": "ModManifest",
+ "description": "A mod as the platform knows it (ADR-013): who publishes it, which runtime runs it, which capabilities it asks for and the resources it may use. Requested capabilities are only a request: the install's approved subset is the grant, and trust tiers are install metadata that never change a grant check. The runtime-specific payload (a data pack, a script bundle) is not part of the manifest.",
+ "type": "object",
+ "required": [
+ "id",
+ "name",
+ "version",
+ "publisher",
+ "target",
+ "runtime",
+ "permissions",
+ "resources",
+ "compatibility"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^mod_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "Stable mod id; its principal subject is mod:<id>."
+ },
+ "name": { "type": "string", "minLength": 1, "maxLength": 80 },
+ "version": {
+ "type": "string",
+ "pattern": "^(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(-[0-9A-Za-z.-]+)?$",
+ "description": "Semantic version of this release."
+ },
+ "description": { "type": "string", "maxLength": 1000 },
+ "publisher": {
+ "$ref": "../identity/subject-ref.v1.json",
+ "description": "Who publishes the mod (a user or an app subject)."
+ },
+ "target": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9-]{1,39}\\.[a-z][a-z0-9-]{1,39}$",
+ "description": "Where the mod runs: <service>.<surface>, e.g. games.browser, games.source, live.overlay."
+ },
+ "runtime": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9-]{1,39}@[1-9][0-9]{0,3}$",
+ "description": "Runtime adapter and its major version, e.g. games-content@1 (declarative data pack) or source-quickjs@1."
+ },
+ "permissions": {
+ "type": "object",
+ "required": ["capabilities"],
+ "additionalProperties": false,
+ "properties": {
+ "capabilities": {
+ "description": "Capability ids the mod asks for (3+ segments). The target runtime binds only those it implements, and only once granted.",
+ "type": "array",
+ "maxItems": 64,
+ "uniqueItems": true,
+ "items": { "type": "string", "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+){2,}$" }
+ },
+ "events": {
+ "description": "Event types the mod wants delivered to it.",
+ "type": "array",
+ "maxItems": 64,
+ "uniqueItems": true,
+ "items": { "type": "string", "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+){2,}$" }
+ },
+ "modules": {
+ "description": "User-module namespaces the mod wants to read or write (a trailing .* names a family).",
+ "type": "array",
+ "maxItems": 32,
+ "uniqueItems": true,
+ "items": { "type": "string", "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)*(\\.\\*)?$" }
+ },
+ "mediaNamespaces": {
+ "description": "Media namespaces the mod wants to read or write.",
+ "type": "array",
+ "maxItems": 32,
+ "uniqueItems": true,
+ "items": { "type": "string", "pattern": "^[a-z][a-z0-9_-]*(\\.[a-z0-9_-]+)*$" }
+ }
+ }
+ },
+ "resources": {
+ "type": "object",
+ "required": ["cpuMs", "memoryMb", "storageMb"],
+ "additionalProperties": false,
+ "description": "The budget the mod asks for. Runtimes meter it; the sandbox that enforces it lives in OpenVibe.Host (Stage C).",
+ "properties": {
+ "cpuMs": {
+ "type": "number",
+ "minimum": 0,
+ "maximum": 1000,
+ "description": "CPU milliseconds per tick (game runtimes) or per request."
+ },
+ "memoryMb": { "type": "integer", "minimum": 0, "maximum": 4096 },
+ "storageMb": { "type": "integer", "minimum": 0, "maximum": 102400 },
+ "outboundHosts": {
+ "description": "Hosts the mod may reach over the network. Empty or absent = none.",
+ "type": "array",
+ "maxItems": 32,
+ "uniqueItems": true,
+ "items": {
+ "type": "string",
+ "pattern": "^(?=.{1,253}$)([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z]{2,63}$"
+ }
+ }
+ }
+ },
+ "assets": {
+ "description": "Assets the mod ships, as Media object references.",
+ "type": "array",
+ "maxItems": 256,
+ "items": { "$ref": "../media/media-ref.v1.json" }
+ },
+ "compatibility": {
+ "type": "object",
+ "required": ["runtime"],
+ "additionalProperties": false,
+ "properties": {
+ "runtime": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 100,
+ "description": "Semver range of the runtime this release works with, e.g. \">=1.0.0 <2.0.0\"."
+ },
+ "contracts": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 100,
+ "description": "Semver range of openvibe-contracts releases it was built against."
+ }
+ }
+ },
+ "homepage": { "type": "string", "pattern": "^https://[^\\s]{1,2000}$" }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "id": "mod_01JABCDEFGHJKMNPQRSTVWXYZ0", "name": "X", "version": "1.0.0", "publisher": { "type": "user", "id": "usr_01JABCDEFGHJKMNPQRSTVWXYZ0" }, "target": "games.browser", "runtime": "games-content@1", "resources": { "cpuMs": 1, "memoryMb": 0, "storageMb": 0 }, "compatibility": { "runtime": ">=1.0.0" } }
new file mode 100644
new file mode 100644
@@ -0,0 +1,12 @@
@@ -0,0 +1,12 @@
+{
+ "id": "mod_01JABCDEFGHJKMNPQRSTVWXYZ0",
+ "name": "Town Square",
+ "version": "1.0.0",
+ "description": "A public workbench and a greeting.",
+ "publisher": { "type": "user", "id": "usr_01JABCDEFGHJKMNPQRSTVWXYZ0" },
+ "target": "games.browser",
+ "runtime": "games-content@1",
+ "permissions": { "capabilities": ["games.world.announce", "games.prop.place"] },
+ "resources": { "cpuMs": 1, "memoryMb": 0, "storageMb": 0 },
+ "compatibility": { "runtime": ">=1.0.0 <2.0.0" }
+}
@@ -1620,6 +1620,185 @@
@@ -1620,6 +1620,185 @@
}
}
}
}
}
}
+ },
+ "mods.mod-manifest": {
+ "version": "1.0.0",
+ "owner": "contracts",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/mods/mod-manifest.v1.json",
+ "title": "ModManifest",
+ "description": "A mod as the platform knows it (ADR-013): who publishes it, which runtime runs it, which capabilities it asks for and the resources it may use. Requested capabilities are only a request: the install's approved subset is the grant, and trust tiers are install metadata that never change a grant check. The runtime-specific payload (a data pack, a script bundle) is not part of the manifest.",
+ "type": "object",
+ "required": [
+ "id",
+ "name",
+ "version",
+ "publisher",
+ "target",
+ "runtime",
+ "permissions",
+ "resources",
+ "compatibility"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^mod_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "Stable mod id; its principal subject is mod:<id>."
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 80
+ },
+ "version": {
+ "type": "string",
+ "pattern": "^(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(-[0-9A-Za-z.-]+)?$",
+ "description": "Semantic version of this release."
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 1000
+ },
+ "publisher": {
+ "$ref": "../identity/subject-ref.v1.json",
+ "description": "Who publishes the mod (a user or an app subject)."
+ },
+ "target": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9-]{1,39}\\.[a-z][a-z0-9-]{1,39}$",
+ "description": "Where the mod runs: <service>.<surface>, e.g. games.browser, games.source, live.overlay."
+ },
+ "runtime": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9-]{1,39}@[1-9][0-9]{0,3}$",
+ "description": "Runtime adapter and its major version, e.g. games-content@1 (declarative data pack) or source-quickjs@1."
+ },
+ "permissions": {
+ "type": "object",
+ "required": [
+ "capabilities"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "capabilities": {
+ "description": "Capability ids the mod asks for (3+ segments). The target runtime binds only those it implements, and only once granted.",
+ "type": "array",
+ "maxItems": 64,
+ "uniqueItems": true,
+ "items": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+){2,}$"
+ }
+ },
+ "events": {
+ "description": "Event types the mod wants delivered to it.",
+ "type": "array",
+ "maxItems": 64,
+ "uniqueItems": true,
+ "items": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+){2,}$"
+ }
+ },
+ "modules": {
+ "description": "User-module namespaces the mod wants to read or write (a trailing .* names a family).",
+ "type": "array",
+ "maxItems": 32,
+ "uniqueItems": true,
+ "items": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)*(\\.\\*)?$"
+ }
+ },
+ "mediaNamespaces": {
+ "description": "Media namespaces the mod wants to read or write.",
+ "type": "array",
+ "maxItems": 32,
+ "uniqueItems": true,
+ "items": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9_-]*(\\.[a-z0-9_-]+)*$"
+ }
+ }
+ }
+ },
+ "resources": {
+ "type": "object",
+ "required": [
+ "cpuMs",
+ "memoryMb",
+ "storageMb"
+ ],
+ "additionalProperties": false,
+ "description": "The budget the mod asks for. Runtimes meter it; the sandbox that enforces it lives in OpenVibe.Host (Stage C).",
+ "properties": {
+ "cpuMs": {
+ "type": "number",
+ "minimum": 0,
+ "maximum": 1000,
+ "description": "CPU milliseconds per tick (game runtimes) or per request."
+ },
+ "memoryMb": {
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 4096
+ },
+ "storageMb": {
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 102400
+ },
+ "outboundHosts": {
+ "description": "Hosts the mod may reach over the network. Empty or absent = none.",
+ "type": "array",
+ "maxItems": 32,
+ "uniqueItems": true,
+ "items": {
+ "type": "string",
+ "pattern": "^(?=.{1,253}$)([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z]{2,63}$"
+ }
+ }
+ }
+ },
+ "assets": {
+ "description": "Assets the mod ships, as Media object references.",
+ "type": "array",
+ "maxItems": 256,
+ "items": {
+ "$ref": "../media/media-ref.v1.json"
+ }
+ },
+ "compatibility": {
+ "type": "object",
+ "required": [
+ "runtime"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "runtime": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 100,
+ "description": "Semver range of the runtime this release works with, e.g. \">=1.0.0 <2.0.0\"."
+ },
+ "contracts": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 100,
+ "description": "Semver range of openvibe-contracts releases it was built against."
+ }
+ }
+ },
+ "homepage": {
+ "type": "string",
+ "pattern": "^https://[^\\s]{1,2000}$"
+ }
+ }
+ }
}
}
}
}
}
}
@@ -3198,3 +3198,89 @@ export interface SourceItem {
@@ -3198,3 +3198,89 @@ export interface SourceItem {
change_seq?: number;
change_seq?: number;
revisions?: unknown[];
revisions?: unknown[];
}
}
+
+/** [email protected] (owner: contracts) */
+/**
+ * A mod as the platform knows it (ADR-013): who publishes it, which runtime runs it, which capabilities it asks for and the resources it may use. Requested capabilities are only a request: the install's approved subset is the grant, and trust tiers are install metadata that never change a grant check. The runtime-specific payload (a data pack, a script bundle) is not part of the manifest.
+ */
+export interface ModManifest {
+ /**
+ * Stable mod id; its principal subject is mod:<id>.
+ */
+ id: string;
+ name: string;
+ /**
+ * Semantic version of this release.
+ */
+ version: string;
+ description?: string;
+ publisher: SubjectRef;
+ /**
+ * Where the mod runs: <service>.<surface>, e.g. games.browser, games.source, live.overlay.
+ */
+ target: string;
+ /**
+ * Runtime adapter and its major version, e.g. games-content@1 (declarative data pack) or source-quickjs@1.
+ */
+ runtime: string;
+ permissions: {
+ /**
+ * Capability ids the mod asks for (3+ segments). The target runtime binds only those it implements, and only once granted.
+ *
+ * @maxItems 64
+ */
+ capabilities: string[];
+ /**
+ * Event types the mod wants delivered to it.
+ *
+ * @maxItems 64
+ */
+ events?: string[];
+ /**
+ * User-module namespaces the mod wants to read or write (a trailing .* names a family).
+ *
+ * @maxItems 32
+ */
+ modules?: string[];
+ /**
+ * Media namespaces the mod wants to read or write.
+ *
+ * @maxItems 32
+ */
+ mediaNamespaces?: string[];
+ };
+ /**
+ * The budget the mod asks for. Runtimes meter it; the sandbox that enforces it lives in OpenVibe.Host (Stage C).
+ */
+ resources: {
+ /**
+ * CPU milliseconds per tick (game runtimes) or per request.
+ */
+ cpuMs: number;
+ memoryMb: number;
+ storageMb: number;
+ /**
+ * Hosts the mod may reach over the network. Empty or absent = none.
+ *
+ * @maxItems 32
+ */
+ outboundHosts?: string[];
+ };
+ /**
+ * Assets the mod ships, as Media object references.
+ *
+ * @maxItems 256
+ */
+ assets?: MediaRef[];
+ compatibility: {
+ /**
+ * Semver range of the runtime this release works with, e.g. ">=1.0.0 <2.0.0".
+ */
+ runtime: string;
+ /**
+ * Semver range of openvibe-contracts releases it was built against.
+ */
+ contracts?: string;
+ };
+ homepage?: string;
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,28 @@
@@ -0,0 +1,28 @@
+{
+ "id": "games.mod.manage",
+ "version": "1.0",
+ "owner": "games",
+ "status": "active",
+ "visibility": "first-party",
+ "description": "Manage mod installs in the browser game's registry (ADR-013): install a mods/mod-manifest.v1 manifest with its games-content@1 pack, approve or revoke individual capabilities of an install, enable, disable, and revoke an install for good (its effects end on the next server tick). Every action is audited and published as a games.mod.* event. Trust tiers are metadata and never grant anything. Also accepted from an openvibe.network owner/admin session.",
+ "inputSchema": "mods.mod-manifest@1",
+ "permissions": ["games:mods:admin"],
+ "resourceConstraints": ["none"],
+ "quotaClass": "games-admin",
+ "events": [
+ "games.mod.installed",
+ "games.mod.enabled",
+ "games.mod.disabled",
+ "games.mod.grants_changed",
+ "games.mod.revoked"
+ ],
+ "implementedBy": [
+ "POST /api/v1/mods",
+ "POST /api/v1/mods/:id/enable",
+ "POST /api/v1/mods/:id/disable",
+ "POST /api/v1/mods/:id/revoke",
+ "POST /api/v1/mods/:id/grants",
+ "DELETE /api/v1/mods/:id/grants/:capability",
+ "GET /api/v1/mods/:id/audit"
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,14 @@
@@ -0,0 +1,14 @@
+{
+ "id": "games.mod.read",
+ "version": "1.0",
+ "owner": "games",
+ "status": "active",
+ "visibility": "public",
+ "description": "Read the browser game's installed mods: manifest, content pack, status, trust tier, requested and granted capabilities. Public; no token needed.",
+ "outputSchema": "mods.mod-manifest@1",
+ "permissions": ["games:mods:read"],
+ "resourceConstraints": ["none"],
+ "quotaClass": "games-read",
+ "events": [],
+ "implementedBy": ["GET /api/v1/mods", "GET /api/v1/mods/:id"]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,15 @@
@@ -0,0 +1,15 @@
+{
+ "id": "games.prop.place",
+ "version": "1.0",
+ "owner": "games",
+ "status": "active",
+ "visibility": "public",
+ "description": "Runtime binding for mods (games-content@1): place and remove inert props (existing content items without health, storage, shop, machine or vehicle-part capabilities) in the world, owned by the mod so players cannot pick them up or move them. The props exist only while the capability is granted and the install enabled: revoking either despawns them on the next tick.",
+ "permissions": ["games:world:place"],
+ "resourceConstraints": ["none"],
+ "quotaClass": "games-mod-runtime",
+ "events": [],
+ "implementedBy": [
+ "mod runtime binding ModApi.placeProp / ModApi.removeProp (apps/server/src/mods/runtime.ts)"
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
+{
+ "id": "games.world.announce",
+ "version": "1.0",
+ "owner": "games",
+ "status": "active",
+ "visibility": "public",
+ "description": "Runtime binding for mods (games-content@1): broadcast a short server announcement to every player in the world, prefixed with the mod's name, at most once a minute per announcement. Checked at call time against the install's approved capabilities; a revoked grant stops it on the next tick.",
+ "permissions": ["games:world:announce"],
+ "resourceConstraints": ["none"],
+ "quotaClass": "games-mod-runtime",
+ "events": [],
+ "implementedBy": ["mod runtime binding ModApi.announce (apps/server/src/mods/runtime.ts)"]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,14 @@
@@ -0,0 +1,14 @@
+{
+ "id": "mods.grant.manage",
+ "version": "1.0",
+ "owner": "network",
+ "status": "planned",
+ "visibility": "first-party",
+ "description": "Planned (ADR-013, grants in Network): create the `mod` principal (mod:mod_<ULID>) for an install and approve or revoke the subset of its manifest's requested capabilities, so a mod's grants are checked like any other principal's. Until Network implements it, each runtime keeps the approved subset itself (OpenVibe.Games: mod_grants).",
+ "inputSchema": "mods.mod-manifest@1",
+ "permissions": ["network:grants:admin"],
+ "resourceConstraints": ["none"],
+ "quotaClass": "network-admin",
+ "events": [],
+ "implementedBy": []
+}
@@ -1,22 +1,34 @@
@@ -1,22 +1,34 @@
{
{
- "version": "0.1.0",
+ "version": "0.2.0",
"contractRanges": {
"contractRanges": {
- "openvibe-contracts": ">=0.2.0 <1.0.0"
+ "openvibe-contracts": ">=0.8.0 <1.0.0"
},
},
"id": "games",
"id": "games",
"name": "OpenVibe.Games",
"name": "OpenVibe.Games",
"status": "stable",
"status": "stable",
"repository": "OpenVibers/OpenVibe.Games",
"repository": "OpenVibers/OpenVibe.Games",
- "domains": [
- "openvibe.games",
- "play.openvibe.games"
- ],
+ "domains": ["openvibe.games", "play.openvibe.games"],
"publicOrigin": "https://openvibe.games",
"publicOrigin": "https://openvibe.games",
"health": "/healthz",
"health": "/healthz",
- "capabilities": [],
- "eventsProduced": [],
+ "capabilities": [
+ "games.mod.manage",
+ "games.mod.read",
+ "games.world.announce",
+ "games.prop.place"
+ ],
+ "eventsProduced": [
+ "games.player.joined",
+ "games.player.left",
+ "games.skill.leveled",
+ "games.blueprint.unlocked",
+ "games.world.saved",
+ "games.mod.installed",
+ "games.mod.enabled",
+ "games.mod.disabled",
+ "games.mod.grants_changed",
+ "games.mod.revoked"
+ ],
"eventsConsumed": [],
"eventsConsumed": [],
- "namespacesOwned": [
- "games.*"
- ]
+ "namespacesOwned": ["games.*"],
+ "notes": "Scraplandia (browser runtime). Players are keyed by canonical Network subjects (ADR-0006 in the repo). Calls Events (events.event.publish), Media (media.object.upload, namespace games: map-editor assets) and Network identity (identity.subject.resolve, batch legacy adoption) with the games client-credentials principal. Mods: games-content@1 declarative packs only; executable mods wait for OpenVibe.Host Stage C. No achievement system exists, so there are no games.achievement.* events."
}
}
@@ -24,7 +24,8 @@
@@ -24,7 +24,8 @@
"network.coins.transfer",
"network.coins.transfer",
"network.notifications.push",
"network.notifications.push",
"network.modules.read",
"network.modules.read",
- "network.modules.write"
+ "network.modules.write",
+ "mods.grant.manage"
],
],
"eventsProduced": [],
"eventsProduced": [],
"eventsConsumed": [],
"eventsConsumed": [],
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
{
{
"name": "openvibe-contracts",
"name": "openvibe-contracts",
- "version": "0.13.0",
+ "version": "0.14.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",