This guide describes how to package a community plugin, host it on GitHub Releases, and submit its listing to the Chattree Marketplace. Official plugins follow a separate maintainer workflow. Portal/API behavior and app update discovery can change; verify against the deployed service before announcing a release.
Plugins are distributed as .chattree-plugin ZIP packages, commonly attached to GitHub Releases, and registered through the web portal or REST API. In addition to WASM SHA-256 integrity checks, the Chattree runtime supports optional Ed25519 digital signature verification (validated when signature.json is present), persistent anti-downgrade watermarks, and atomic transaction rollback.
Chattree enforces a rigorous multi-layered trust model for plugins installed from the Marketplace:
wasmSha256):
plugin.json manifest declares the exact SHA-256 digest of plugin.wasm.chattree-official-v1), binding the developer's public key, identity (developerId), and validity window (validFrom / validUntil).WasmExecutionAuthorization) referentially bound to the production PluginPackageManager.instance. Fake or isolated test tokens are rejected.┌─────────────────────────────────────────────────────────────┐
│ 1. Code & Compile │
│ Write source code (WAT, Rust, C) and compile plugin.wasm │
└──────────────────────────────┬──────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 2. Configure Manifest (plugin.json) │
│ Define id, version (SemVer), capabilities, and changelog │
└──────────────────────────────┬──────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 3. Build .chattree-plugin Package │
│ ZIP bundle containing plugin.json, plugin.wasm, and icon │
└──────────────────────────────┬──────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 4. Create GitHub Release │
│ Publish tag (e.g. v1.0.2) and attach .chattree-plugin │
└──────────────────────────────┬──────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 5. Register with Marketplace (Web Portal or REST API) │
│ Submit the public release download URL for validation │
└──────────────────────────────┬──────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 6. Catalog listing and in-app discovery (verify deployment) │
│ Available in Chattree app: One-tap install or update │
└─────────────────────────────────────────────────────────────┘
Host your plugin code in a public GitHub repository. Standard layout:
my-plugin/
├── src/ # Source code (WAT, Rust, C, etc.)
├── plugin.json # Required plugin manifest
├── plugin.wasm # Compiled WebAssembly binary
├── icon.png # 512x512 PNG icon (transparent or dark bg)
└── README.md # Documentation and usage instructions
plugin.json)The plugin.json file is the contract that defines your plugin's identity, permissions, commands, and security boundaries.
{
"schemaVersion": 1,
"id": "com.example.weather",
"version": "1.0.0",
"minAppVersion": "1.0.0",
"name": "Live Weather",
"description": "Real-time weather forecasts and alerts.",
"author": "Acme Developer",
"authorEmail": "developer@example.com",
"homepageUrl": "https://example.com/weather",
"primaryCommand": "weather",
"aliases": ["clima", "forecast"],
"type": "imported",
"capabilities": [
"network.http",
"location.current"
],
"changelog": "1.0.0: Initial public release.",
"runtime": "wasm",
"entrypoint": "weather_entry",
"wasmPath": "plugin.wasm",
"wasmSha256": "62d79bbba364d13b49f3eba731b6f05974917e16ffd154fc3036ea536ce16310",
"runtimeApiVersion": "1.0.0",
"maxMemoryPages": 4,
"timeoutMs": 5000,
"fuelLimit": 50000000,
"tools": [
{
"id": "com.example.weather.current",
"command": "weather",
"displayName": "Check Current Weather",
"description": "Get current weather: /weather [city]",
"capabilities": [
"network.http",
"location.current"
]
}
]
}
Rules:
id: Must be a unique reverse-domain identifier (e.g.com.developer.plugin-name).version: Must follow Semantic Versioning (MAJOR.MINOR.PATCH).wasmSha256: Must exactly match the SHA-256 hash (in lowercase) of the compiledplugin.wasm.
.chattree-plugin PackageA .chattree-plugin package is a standard ZIP archive containing the manifest, the compiled WebAssembly binary, and an optional icon at the root level.
The easiest and safest way to bundle your plugin is using @chattree/cli. It automatically checks your manifest, synchronizes wasmSha256, and packages files directly at the archive root:
# In your plugin project directory:
npx @chattree/cli package
This outputs <id>-v<version>.chattree-plugin ready to host on GitHub Releases.
CRITICAL: All files must reside directly in the root of the ZIP archive. Do not package them inside an enclosing subfolder.
# In the directory containing plugin.json, plugin.wasm, and icon.png:
Compress-Archive -Path plugin.json, plugin.wasm, icon.png -DestinationPath weather-v1.0.0.zip -Force
Rename-Item "weather-v1.0.0.zip" "weather-v1.0.0.chattree-plugin"
zip -9 weather-v1.0.0.chattree-plugin plugin.json plugin.wasm icon.png
https://github.com/<owner>/<repo>/releases).v1.0.0).Weather Plugin v1.0.0)..chattree-plugin file (e.g. weather-v1.0.0.chattree-plugin).After publication, copy the direct asset URL from the release assets list. The format is:
https://github.com/<owner>/<repo>/releases/download/v1.0.0/weather-v1.0.0.chattree-plugin
You can publish your plugin through either the web interface or the REST API.
.chattree-plugin file.plugin.json, validates the WASM header and SHA-256, and checks declared capabilities against its catalog. This is structural validation, not a code security audit.https://raw.githubusercontent.com/<owner>/<repo>/main/icon.png
You can publish programmatically via the submission endpoint:
POST https://chattree.chat/api/plugins/submit
Content-Type: application/json
| Header | Value | Required | Description |
|---|---|---|---|
Content-Type | application/json | ✅ | Must specify JSON body |
User-Agent | your-tool/1.0 | ⚪ | Recommended identifier |
| Parameter | Type | Required | Description |
|---|---|---|---|
download_url | string (URL) | ✅ | Direct HTTPS download URL to the .chattree-plugin asset attached to a GitHub Release. |
icon_url | string (URL) | ✅ | Public HTTPS URL for the 512x512 plugin icon, strictly hosted on GitHub domains (raw.githubusercontent.com, user-images.githubusercontent.com, github.com). |
banner_url | string (URL) | ⚪ | Optional promotional banner image (1200x630 px) hosted on GitHub. |
source_url | string (URL) | ⚪ | Public GitHub repository URL containing the plugin source code. |
homepage_url | string (URL) | ⚪ | Project or author website URL. |
author_url | string (URL) | ⚪ | GitHub profile or personal page of the developer. |
license | string | ⚪ | SPDX license identifier (default: "MIT"). |
category | string | ⚪ | One of: productivity, utilities, devtools, ai, automation, files, communication, media, education, other (default: "utilities"). |
name | string | ⚪ | Overrides manifest display name if desired. |
short_description | string | ⚪ | Brief summary shown in mobile cards (max 120 chars). |
description | string | ⚪ | Extended description shown on the plugin details page. |
changelog | string | ⚪ | Release notes describing the updates in this version. |
{
"download_url": "https://github.com/<owner>/<repo>/releases/download/v1.0.0/weather-v1.0.0.chattree-plugin",
"icon_url": "https://raw.githubusercontent.com/<owner>/<repo>/main/icon.png",
"source_url": "https://github.com/<owner>/<repo>",
"category": "utilities",
"license": "MIT",
"changelog": "1.0.0: Initial public release."
}
$body = @{
download_url = "https://github.com/<owner>/<repo>/releases/download/v1.0.0/weather-v1.0.0.chattree-plugin"
icon_url = "https://raw.githubusercontent.com/<owner>/<repo>/main/icon.png"
source_url = "https://github.com/<owner>/<repo>"
category = "utilities"
license = "MIT"
} | ConvertTo-Json
Invoke-RestMethod -Uri "https://chattree.chat/api/plugins/submit" `
-Method POST `
-ContentType "application/json" `
-Body $body
curl -X POST https://chattree.chat/api/plugins/submit \
-H "Content-Type: application/json" \
-d '{
"download_url": "https://github.com/<owner>/<repo>/releases/download/v1.0.0/weather-v1.0.0.chattree-plugin",
"icon_url": "https://raw.githubusercontent.com/<owner>/<repo>/main/icon.png",
"source_url": "https://github.com/<owner>/<repo>",
"category": "utilities",
"license": "MIT"
}'
| Status Code | Code Identifier | Description |
|---|---|---|
200 OK | (Success) | Package successfully downloaded from GitHub, validated, and registered in the catalog. |
400 Bad Request | MISSING_DOWNLOAD_URL | The download_url property was missing or empty. |
400 Bad Request | MISSING_ICON_URL | The icon_url property was missing or empty. |
400 Bad Request | INVALID_ICON_HOST | The icon URL is not hosted on an allowed GitHub domain. |
400 Bad Request | GITHUB_FETCH_FAILED | Failed to download package from GitHub Release (verify asset is public). |
400 Bad Request | PACKAGE_TOO_LARGE | The package exceeds the maximum limit of 25 MB. |
400 Bad Request | VALIDATION_FAILED | Package failed structural or integrity validation (see details array). |
409 Conflict | VERSION_ALREADY_EXISTS | The exact plugin ID and version are already registered. |
409 Conflict | VERSION_NOT_NEWER | The submitted version is not strictly greater than the highest registered SemVer for this plugin. |
429 Too Many Requests | RATE_LIMIT_EXCEEDED | Production process-local limiter observed more than 5 submissions in an hour for the IP; multi-instance deployments may not share this counter. |
500 Internal Error | SERVER_ERROR | Internal database or registry failure. |
200 OK){
"success": true,
"submissionId": "5329803a-25ef-41ad-bfdd-6baba3e78e91",
"status": "published",
"message": "Plugin registered and published successfully. It is now live and available in the Chattree mobile app.",
"plugin": {
"id": "com.example.weather",
"slug": "live-weather",
"version": "1.0.0",
"name": "Live Weather",
"category": "utilities",
"downloadUrl": "https://github.com/.../weather-v1.0.0.chattree-plugin",
"packageSize": 1436,
"sha256": "07298334903f19a2ff897156103187781292e36d742b31f78315ef7a1a088a68",
"capabilities": ["network.http", "location.current"]
},
"warnings": []
}
400 / 422 Unprocessable Entity){
"error": "Plugin package validation failed.",
"code": "VALIDATION_FAILED",
"details": [
"WASM SHA-256 checksum mismatch! The hash in plugin.json does not match plugin.wasm."
],
"warnings": [
"Declared capability \"custom.sensor\" is not recognized in the standard catalogue."
]
}
Releasing an update to an existing plugin follows a clean, automated lifecycle.
Implement bug fixes, performance optimizations, or new capabilities in your code.
Recompile plugin.wasm using your toolchain (e.g. wat2wasm, cargo build --target wasm32-unknown-unknown, etc.).
plugin.jsonOpen plugin.json and apply the changes:
version string following SemVer (e.g., 1.0.0 → 1.0.1 or 1.1.0).wasmSha256 with the new SHA-256 hash of plugin.wasm.changelog with a concise summary of the update.{
"id": "com.example.weather",
"version": "1.0.1",
"wasmSha256": "new_sha256_hash_of_plugin_wasm...",
"changelog": "1.0.1: Fixed city query parsing and improved offline caching."
}
Generate the new package file named with the new version:
Compress-Archive -Path plugin.json, plugin.wasm, icon.png -DestinationPath weather-v1.0.1.zip -Force
Rename-Item "weather-v1.0.1.zip" "weather-v1.0.1.chattree-plugin"
git add .
git commit -m "chore: bump version to 1.0.1"
git push origin main
v1.0.1.weather-v1.0.1.chattree-plugin to the release.Submit the new download URL through the Web Submission Portal or via the REST API (POST /api/plugins/submit):
$body = @{
download_url = "https://github.com/<owner>/<repo>/releases/download/v1.0.1/weather-v1.0.1.chattree-plugin"
icon_url = "https://raw.githubusercontent.com/<owner>/<repo>/main/icon.png"
source_url = "https://github.com/<owner>/<repo>"
author_url = "https://github.com/<owner>"
organization = "Acme"
} | ConvertTo-Json
Invoke-RestMethod -Uri "https://chattree.chat/api/plugins/submit" `
-Method POST `
-ContentType "application/json" `
-Body $body
When an update is submitted:
409 VERSION_ALREADY_EXISTS) and enforces strict SemVer monotonicity (409 VERSION_NOT_NEWER if the new version is not strictly greater than all existing versions).POST /api/plugins/submit requires download_url and icon_url; the icon must use HTTPS and an allowed GitHub host. banner_url, source_url, author_url, homepage_url, organization, license, category, listing text, and changelog are optional.source_url; fields such as repository_url, publisher_name, and publisher_email in older examples are not consumed by the current route.status: "draft" when Supabase is not configured (validation-only dry run), and status: "published" after successful database registration. Do not interpret dry-run success as a live listing.| Check | Requirement | Reason |
|---|---|---|
| ✅ No Subfolders | plugin.json and plugin.wasm at ZIP root | Chattree extractor looks at root entries only. |
| ✅ Version Sync | Manifest version matches GitHub Release tag | Prevents version mismatch errors during validation. |
| ✅ WASM Hash Sync | wasmSha256 in manifest matches plugin.wasm | Detects mismatched binary contents; it is not publisher authentication. |
| ✅ Version discipline | Publish a strictly higher SemVer for each update | Enforced by the submit API (409 VERSION_NOT_NEWER). |
| ✅ Raw Image URLs | Use raw.githubusercontent.com for icons | Ensures public accessibility without HTML page wrappers. |
| ✅ Minimal Capabilities | Only declare capabilities actually used | Excessive permissions lower community adoption and trust. |