Skip to content

Manifest reference

manifest.json is the contract between a plugin and the runtime. It is validated at load against packages/shared/src/manifest.ts; an invalid manifest means the plugin is skipped. Unknown top-level fields are rejected (typo protection), so the table below is the complete allowed set.

Minimal example

json
{
  "name": "guestbook",
  "version": "0.1.0",
  "api_version": "^1.0",
  "author": "you",
  "description": "A simple server guestbook.",
  "type": "standalone",
  "backend": { "entry": "backend/index.ts" },
  "frontend": { "entry": "frontend/index.html" },
  "permissions": ["data.sql:self", "broadcast.clients"]
}

Top-level fields

FieldTypeRequiredNotes
namestringSlug. Must match ^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$ — lowercase, starts with a letter, no leading/trailing/consecutive hyphens. This is the plugin's identity everywhere.
versionstringStrict semver MAJOR.MINOR.PATCH. No pre-release/build metadata.
api_versionstringRuntime-API compatibility range, e.g. ^1.0.
authorstringNon-empty.
descriptionstringNon-empty.
type"core" | "standalone" | "extension"See Plugin type.
permissionsstring[]Capability strings the runtime will allow. May be empty for a frontend-only plugin. See Permissions.
extendsstringconditionalRequired iff type: "extension"; forbidden otherwise. The base plugin's slug.
backend{ entry: string }conditionalBackend entry path. At least one of backend/frontend required.
frontend{ entry: string }conditionalFrontend entry path (HTML).
licensestringoptionale.g. "MIT".
iconstringoptionallucide-icon name (e.g. "Hash"). Max 64 chars. Unknown names render a placeholder.
settingsPluginSetting[]optionalAdmin-configurable settings. See Settings.
sidebarobjectoptionalSidebar contribution. See Sidebar.
public_schemaRecord<string, { columns, description }>optionalTables/columns exposed for cross-plugin reads. See public_schema.
dependenciesRecord<slug, semverRange>optionalOther plugins this one depends on.
resources{ memory_mb?, cpu_weight?, disk_mb? }optionalResource hints. Positive integers.
proxy_mountsProxyMount[]optionalReverse-proxy mounts. See proxy_mounts and the reverse-proxy guide.
serve_ready_handshakebooleanoptionalOpt into the two-stage readiness handshake. See Lifecycle. Default false.
client_capabilitiesstring[]optionalClient platform requirements. V1: only "client.browser".
runtime_capabilitiesstring[]optionalRuntime opt-ins: "voice.media", "voice.screen_share", "voice.moderation". Unknown values rejected.
managed_servicesstring[]optionalSidecar services the runtime supervises. Recognized: "livekit".

Plugin type & extends

typeMeaningextends
coreShipped by UnCorded (text-channels, voice-channels, members, moderation).forbidden
standaloneThird-party plugin with its own functionality and data.forbidden
extensionThird-party plugin that extends a base plugin.required — the base plugin slug

Most third-party plugins are standalone.

Settings

Each entry in settings[] is rendered as a form field in Server settings and is readable via plugin.settings.

json
{
  "key": "max_message_length",
  "label": "Max message length",
  "description": "Maximum characters allowed per message.",
  "type": "number",
  "default": 5000,
  "stops": [
    { "value": 2000, "label": "2k" },
    { "value": 5000, "label": "5k" },
    { "value": 0,    "label": "Unlimited" }
  ]
}
FieldTypeApplies toNotes
keystringallUnique within the plugin. Max 256 chars.
labelstringallShown in the admin panel.
descriptionstringallOptional help text.
type"string" | "secret" | "number" | "boolean"secret values are redacted from logs and masked in the UI.
requiredbooleanallSurfaced as a warning if unset.
defaultstring | number | booleanallMust match type. Used when unset.
min / max / stepnumbernumberBounds and slider step (step > 0).
stops{ value, label }[]numberStepped slider with labelled positions. Stored value is the underlying number (e.g. 0 = "unlimited").
max_lengthnumberstring/secretServer-enforced length cap (positive).
enumstring[]stringRenders a select; default must be a member.

Cross-field validation enforces min ≤ default ≤ max, default length ≤ max_length, defaultenum, and default matching a stops value.

json
{ "sidebar": { "contributes": true, "section": "Chat", "refresh_on": ["text-channels.channel.created"] } }
FieldTypeNotes
contributesbooleanRequired. true if the plugin returns sidebar items.
sectionstringOptional default group name for this plugin's items, used when an item doesn't set its own section.
refresh_onstring[]Event topics that trigger a re-fetch of the plugin's sidebar items.

The items themselves come from the backend's sidebar.items handler, not the manifest. See Plugin anatomy → reserved actions.

public_schema

Declares which of your tables and columns are readable by other plugins (via their data.read capability):

json
{
  "public_schema": {
    "messages": {
      "columns": ["id", "channel_id", "author_id", "content", "created_at"],
      "description": "All messages across all channels."
    }
  }
}

Only listed columns are readable; everything else stays private.

proxy_mounts

json
{
  "proxy_mounts": [
    { "name": "demo", "upstream_setting": "demo_upstream_url", "access": "members" }
  ]
}
FieldTypeNotes
namestringSlug-safe, unique within the plugin. Appears in the URL /proxy/<slug>/<name>/*.
upstream_settingstringKey of a string/secret setting in this same manifest holding the upstream URL. The manifest never carries the URL directly.
access"members" | "owner"Optional, default "members".
max_frame_bytesintegerOptional. Max WebSocket frame (message) size relayed in either direction, in bytes. A larger frame closes the socket with 1009. Default 65536 (64 KiB); raise it for apps that bulk-sync over a socket (e.g. game state). Must be between 1024 (1 KiB) and 16777216 (16 MiB).

Declaring proxy_mounts requires at least one of proxy.http:self / proxy.websocket:self in permissions. Mounts are disabled until an owner approves them. Full guide: Reverse-proxy plugins.

Validation rules (summary)

  • At least one of backend / frontend.
  • type: "extension"extends present and a valid slug; core/standalone ⇒ no extends.
  • Every permissions entry matches the capability grammar.
  • proxy_mounts[].upstream_setting references a declared string/secret setting; mount names unique; proxy permission present.
  • proxy_mounts[].max_frame_bytes, when present, is an integer in [1024, 16777216].
  • Settings default consistent with type/min/max/max_length/enum/stops.
  • resources.* positive integers; icon ≤ 64 chars; unknown top-level or per-setting fields rejected.

The tests in packages/shared/src/manifest.test.ts are the exhaustive, executable spec.