Browse API 1 documentation
Package and manifest
Package layout, manifest fields, validation, and network allow-lists.
A Grove extension is a directory of static assets with manifest.json at its root. A distributable .grove file is a zip archive of that directory. Mango accepts a manifest at the archive root or inside one wrapping directory.
Manifest example
{
"grove": 1,
"id": "com.example.grove.poll",
"name": "Poll",
"version": "1.0.0",
"description": "Run quick polls in a channel.",
"author": { "name": "Example Developer", "url": "https://example.com" },
"icon": "icon.png",
"readme": "README.md",
"screenshots": [
{ "path": "screenshots/results.webp", "caption": "Poll results in a channel pane." }
],
"entry": "index.html",
"settings": "settings.html",
"presentation": {
"surfaces": ["channelPane", "window"],
"defaultSurface": "channelPane",
"minSize": { "width": 280, "height": 200 },
"defaultSize": { "width": 340, "height": 420 }
},
"capabilities": ["channel.read", "channel.send", "storage"],
"hostPermissions": ["https://api.example.com"]
}
Top-level fields
| Field | Required | Contract |
|---|---|---|
grove |
Yes | Integer API version. Mango 3.1 supports 1. |
id |
Yes | Immutable, globally unique reverse-DNS identifier. It must contain a dot and cannot start or end with one. |
name |
Yes | Human-readable extension name shown by Mango. |
version |
Yes | Release version string. Semantic versions are required for catalogue update ordering. |
description |
No | Short store and consent description. |
author |
No | Object with required name and optional url. |
icon |
No | Package-relative image path. |
readme |
No | Package-relative .md or .markdown file used as the long store description. |
screenshots |
No | Up to eight package images with optional captions of at most 240 characters. |
entry |
Yes | Package-relative HTML entry document. |
settings |
No | Package-relative configuration page opened from Grove Manager. |
presentation |
No | Allowed surfaces, default surface, scope, and sizing hints. Omission defaults to a channel pane. |
contributions |
No | Static metadata for host integrations such as on-demand composer commands and upload providers. |
capabilities |
No | Permission names requested from the user. Unknown names make the manifest invalid. |
hostPermissions |
No | TLS network destinations available to the extension runtime. |
Optional arrays default to empty. Optional presentation defaults to a visible channelPane extension.
Package resource validation
entry, settings, icon, readme, and screenshot paths must name files inside the package. Mango resolves symlinks and rejects:
- absolute or empty resource paths;
..traversal or a symlink that escapes the package;- missing resources or directories where a file is expected;
- a README without a Markdown extension;
- screenshots outside PNG, JPEG, or WebP;
- duplicate screenshot paths;
- more than eight screenshots or an overlong caption.
The package installer also bounds compressed size, expanded size, and entry count. Keep dependencies bundled, remove source maps or test fixtures that the runtime does not need, and do not fetch executable code from a remote host.
Presentation
API 1 manifest surfaces are channelPane, window, tab, composer, and headless. tab, composer, and headless are exclusive contracts. A tab must be the only declared surface and must declare "scope": "server"; Mango rejects a mixed-surface or non-server tab manifest. The valid form creates one visible network-wide instance, and starting it again selects that existing tab. See Surfaces and scopes.
Network destinations
hostPermissions is a network allow-list, separate from native capabilities. Declare the scheme and host:
"hostPermissions": [
"https://api.example.com",
"wss://events.example.com"
]
An https:// entry permits TLS web requests to that host; it does not grant WebSocket access. A wss:// entry permits secure WebSockets and does not grant ordinary HTTPS requests. Cleartext http:// and ws:// entries do not become reachable.
Entries are origin-scoped and may include a port. Exact origins are preferred. A complete leftmost-label wildcard such as https://*.example.com matches one direct subdomain (alice.example.com), but not the apex (example.com), a deeper name (www.alice.example.com), another scheme, or an undeclared port. No other partial wildcard form is supported.
The broad forms https://* and wss://* are reserved for extensions whose destination is genuinely chosen by the user, such as a configurable upload provider. Broad access is displayed prominently during consent and should not be used when the service host is known in advance.
An empty allow-list means no external network access. The extension’s own packaged assets, data:, blob:, and inert browser URLs remain available.
Composer-command contributions
An exclusive composer utility may declare slash commands under contributions.commands. This makes the commands available while the utility is closed; Mango starts the utility only after the user invokes one and then delivers the ordinary command event:
{
"presentation": { "surfaces": ["composer"] },
"contributions": {
"commands": [
{
"name": "gif",
"summary": "Find a GIF and add it to the draft",
"usage": "[search|first] [keywords]",
"subcommands": [
{ "name": "search", "summary": "Open the picker", "usage": "<keywords>" },
{ "name": "first", "summary": "Use the first result", "usage": "<keywords>" }
]
}
]
},
"capabilities": ["commands.register"]
}
The manifest may declare at most 32 commands and 16 subcommands per command. Names are normalized to lowercase, cannot contain whitespace, must be unique inside the package, and cannot shadow a built-in Mango command. summary, usage, and subcommands are optional.
This form is only for the exclusive composer surface. A persistent visible Sprig or headless Root registers commands at runtime with grove.commands.register(...). A composer utility must not call that registration method; its manifest remains the authoritative command list. Command invocation does not grant an IRC send route, and grove.composer.complete(...) still returns text to the draft without sending it.
Upload-provider contributions
Only a headless Root may contribute an upload provider, and it must request uploads.provide:
{
"presentation": { "surfaces": ["headless"], "scope": "application" },
"contributions": {
"uploadProviders": [
{
"id": "primary",
"name": "Private files",
"kind": "userConfigured",
"acceptsImagesOnly": false,
"maxFileSize": 1073741824
}
]
},
"capabilities": ["uploads.provide"]
}
A manifest may declare at most eight providers. A provider identifier is 1–64 lowercase letters, digits, ., _, or -; it must be unique inside the package. kind is userConfigured or thirdParty. maxFileSize is an optional positive byte count.
Application scope is recommended for one destination configured once and available on every network. Use server scope only when the extension deliberately keeps a separate uploader configuration for each IRC session.
See Upload providers for runtime registration.