Manifest Reference
vibelands-plugin.json at the plugin root. Validated by vibelands check, by the server at discovery (invalid manifests are logged and skipped), and by the SDK's validatePluginManifest.
Full example
{
"id": "ambient-butterflies",
"name": "Ambient Butterflies",
"version": "1.2.0",
"apiVersion": 2,
"runtime": "public",
"tier": "A",
"description": "Butterflies that drift over flowers during the day.",
"author": { "name": "Dev", "url": "https://example.dev" },
"license": "MIT",
"entries": {
"client": "client/index.jsx"
},
"assets": "assets/",
"permissions": ["audio"],
"configSchema": {
"type": "object",
"properties": {
"density": { "type": "number", "default": 1, "minimum": 0, "maximum": 3 }
}
},
"editorItems": [
{ "id": "butterfly-marker", "label": "Butterfly Marker", "category": "Nature" }
],
"tags": ["ambient", "decorative"]
}Fields
| Field | Required | Rules |
|---|---|---|
id | ✓ | kebab-case ^[a-z][a-z0-9-]{1,63}$, globally unique. Becomes the namespace for entities (<id>:<suffix>), storage, messages, and asset URLs |
name | ✓ | display name |
version | ✓ | semver; treat as immutable once distributed |
apiVersion | ✓ | must be 2 (the host refuses mismatches at load) |
runtime | - | "public" (default, marketplace/review-gated hosted plugins) or "internal" (workspace/first-party plugins) |
tier | ✓ | "A" (client-only decorative) or "B" (server-authoritative). Tier B requires entries.server |
entries.client | public | path to the React/R3F client source entry (built to dist/client.js for hosted plugins, Vite-bundled for workspace plugins). Public plugins require this field |
entries.server | tier B | path to the server source entry (built to dist/server.js, CJS, Node 20, isolated worker target for hosted public plugins) |
assets | - | informative; the hosted builder ships the assets/ folder when present (≤ 25 MB) |
permissions | - | array of known permission ids - unknown ids fail validation, undeclared capabilities are denied at runtime |
configSchema | - | JSON-schema-lite for per-world config (below) |
editorItems | - | placeable items contributed to the editor Add panel (below) |
description, author, license, tags, minHostVersion | - | metadata for humans and Marketplace listings |
configSchema
A deliberately small subset of JSON Schema - enough for validated config and an auto-generated admin form:
{
"type": "object",
"properties": {
"density": { "type": "number", "default": 1, "minimum": 0, "maximum": 3 },
"rounds": { "type": "integer", "default": 3, "minimum": 1, "maximum": 10 },
"color": { "type": "string", "default": "#7ad7ff" },
"nightOnly":{ "type": "boolean", "default": true },
"mode": { "type": "string", "enum": ["calm", "wild"], "default": "calm" }
}
}Resolution rules (applied by the host before your code sees the config):
- wrong-typed values → replaced by
default - numbers → clamped to
minimum/maximum;integer→ rounded - values outside
enum→ replaced bydefault - unknown keys → dropped
The resolved object is what usePluginConfig() (client) and ctx.config (server) return - both sides always see the same validated values.
editorItems
Items the plugin contributes to the in-game editor's Add library (up to 32 per plugin):
"editorItems": [
{
"id": "decor-chest",
"label": "Decorative Chest",
"category": "Treasure",
"description": "A purely decorative chest.",
"properties": { "title": "Chest", "color": "#7a4a21" },
"shadows": true,
"propertySchema": {
"type": "object",
"properties": {
"title": { "type": "string", "title": "Title", "maxLength": 64 }
}
},
"scale": 1
}
]| Field | Required | Rules |
|---|---|---|
id | ✓ | kebab-case, unique within the plugin; the placed object's preset becomes plugin:<pluginId>:<id> |
label | ✓ | item card display name |
category | - | any string; known editor categories merge in, new ones get their own filter chip. Defaults to Plugins |
preset | - | claim a bare legacy preset id (e.g. tree) instead of the plugin: namespace - existing DB objects with that preset render/behave through this item. First claimer wins; conflicts are logged and skipped |
collider | - | blocking collider for placed objects (server physics + client): { "type": "ball", "radius", "offsetY" }, { "type": "cuboid", "halfExtents", "offsetY" } or { "type": "compound", "pieces": [{ "halfExtents", "offset" }] } (max 8 pieces) |
resource | - | makes the item harvestable: { "kind", "requiredWeaponId", "itemId", "maxHealth", "reward", "respawnMs" }. kind: "tree" gets the host fall-and-stump presentation; itemId must exist in the inventory catalog |
shadows | - | set false for invisible or transparent editor volumes that should not cast or receive host shadows. Defaults to true |
properties | - | default properties merged into every placement (placer values win); ≤ 2 KB |
propertySchema | - | host-rendered per-object fields persisted in properties; supports string, number, integer, boolean, enum, limits, descriptions, and multiline strings |
scale | - | uniform default scale, positive number |
Reference: the first-party forest plugin claims the legacy tree/pine/palm presets with colliders and axe-harvestable wood resources.
Editor items are also part of the in-game AI command vocabulary: players with edit rights can say spawn 10 beacons (matching the item's label or id) and the host spawns the item with its default properties and scale, exactly like an editor placement - but only in worlds where the plugin is enabled. Core preset and vehicle names always win over a plugin item with the same name.
items
Inventory items the plugin contributes to the host item catalog (up to 32 per plugin). They flow through the host inventory - stacking, persistence, phone UI, General Store - exactly like core items:
"items": [
{ "id": "wheat_seed", "name": "Wheat Seed", "category": "Seed",
"maxStack": 99, "shop": { "buyCents": 180 } },
{ "id": "wheat", "name": "Wheat", "category": "Crop",
"maxStack": 999, "shop": { "sellCents": 120 } }
]| Field | Required | Rules |
|---|---|---|
id | ✓ | lowercase id; may claim a legacy core item id (e.g. wheat) so persisted player inventories keep working. First claimer wins |
name | ✓ | display name |
category | - | inventory grouping label; defaults to Item |
maxStack | - | 1–9999, defaults to 99 |
effects | - | applied by the host when the player uses the item: health/hunger/thirst/energy deltas |
shop | - | { "buyCents", "sellCents" } - buyable and/or sellable at the General Store |
Reference: the first-party farming plugin (seeds + crops).
Items render through definePluginClient({ objectRenderers: { 'decor-chest': Component } }). Placements are regular persisted world objects - select/move/duplicate/delete work with zero plugin code, and the server validates that the owning plugin is enabled and declares the item. Optional server hooks: onEditorObjectPlaced(ctx, event) / onEditorObjectRemoved(ctx, event).
The built artifact manifest
The hosted sandbox builder writes a copy of the manifest into the signed artifact with an added integrity block (sha384 per entry). The server recomputes hashes from hosted artifact files at load time - the artifact values are for registries and humans, not trust.