Plugin Development

moss plugins are TypeScript/JavaScript bundles that run in the Tauri webview context. They interact with moss through lifecycle hooks — transforming content, deploying to external services, or syndicating published pages.

Quick start

  1. Copy an existing plugin directory (e.g., plugins/github/)
  2. Update assets/manifest.json with your plugin's details
  3. Implement the hooks your plugin needs
  4. Build with esbuild:
esbuild src/main.ts --bundle --format=iife --outfile=dist/main.bundle.js

Directory structure

my-plugin/
├── assets/
│   ├── manifest.json    Plugin metadata and config
│   └── icon.svg         Plugin icon
├── src/
│   └── main.ts          Plugin source
├── dist/
│   └── main.bundle.js   Built output
└── package.json

Manifest reference

The assets/manifest.json file describes your plugin to moss.

FieldTypeRequiredDescription
namestringyesPlugin identifier
versionsemveryesPlugin version
descriptionstringyesShort description
authorstringyesAuthor name
entrystringyesJS bundle filename
capabilitiesstring[]yesHook types: "process", "deploy", "syndicate"
domainstringnoPrimary service domain
global_namestringyesJavaScript global object name
iconstringnoIcon filename
configobjectnoDefault config values
config_schemaobjectnoConfig field types: "boolean", "number", "string"

Example:

{
  "name": "my-plugin",
  "version": "1.0.0",
  "description": "Publishes to My Service",
  "author": "Your Name",
  "entry": "main.bundle.js",
  "capabilities": ["deploy", "syndicate"],
  "domain": "myservice.example",
  "global_name": "MyPlugin",
  "icon": "icon.svg",
  "config": {
    "auto_publish": false
  },
  "config_schema": {
    "auto_publish": "boolean"
  }
}

Hooks

Declare which hooks your plugin handles in the capabilities array of the manifest, then export them from your bundle's global object.

HookTriggerContext
before_buildBefore HTML generationRaw markdown and frontmatter
on_buildReplaces HTML generationSource files and page tree
on_deployOn deploy actionCompiled site output
configure_domainAfter DNS is setDomain and deployment info
after_deployAfter deployPublished articles and deployment info
const MyPlugin = {
  async on_deploy(ctx: DeployContext): Promise<HookResult> {
    reportProgress('Uploading files...')
    // your deploy logic
    return { success: true }
  },

  async after_deploy(ctx: SyndicateContext): Promise<HookResult> {
    // your syndication logic
    return { success: true }
  }
}

moss-api SDK

The @symbiosis-lab/moss-api package provides types and utilities for plugin development.

npm install @symbiosis-lab/moss-api

Types:

ExportDescription
ProcessContextPassed to before_build hooks
GenerateContextPassed to on_build hooks
DeployContextPassed to on_deploy hooks
ConfigureDomainContextPassed to configure_domain hooks
SyndicateContextPassed to after_deploy hooks
HookResultReturn type for all hooks

Utilities:

ExportDescription
setMessageContextSet context for log messages
reportProgressReport build/deploy progress to the UI
reportErrorReport a non-fatal or fatal error
reportCompleteSignal hook completion
sendMessageSend a message to the moss UI

Browser:

ExportDescription
openBrowserOpen a browser window inside moss
closeBrowserClose a previously opened browser tab

For the full API reference, see @symbiosis-lab/moss-api on GitHub.

Testing

Use vitest with the mock Tauri setup included in the SDK:

import { describe, it, expect } from 'vitest'

describe('my-plugin deploy', () => {
  it('returns success on valid config', async () => {
    const result = await MyPlugin.on_deploy(mockDeployContext)
    expect(result.success).toBe(true)
  })
})

Run tests with:

npx vitest run

Contributing

See the CONTRIBUTING guide for the full development workflow, CI/CD setup, and code style guidelines.

Published with moss