Skip to main content
Plugin SDK

Build plugins that
extend SocialScale.

Write a plugin in Rust, Go, C, or AssemblyScript and compile it to WebAssembly. SocialScale loads it inside a Wasmtime sandbox — safe, isolated, and blazing fast.

Rust Best support
Go Supported
C / C++ Supported
AssemblyScript Supported

What you can build

Five capability types, each targeting a different integration point in the SocialScale pipeline.

⬡ Platform Connector
PlatformConnector

Add support for a new social platform. Implements OAuth, publish, delete, fetch comments, and analytics endpoints.

✦ Content Transformer
ContentTransformer

Transform post content before publishing — translate, reformat for a platform, add watermarks, or apply brand filters.

◈ Policy Evaluator
PolicyEvaluator

Custom compliance and content safety rules that run before every publish. Block, warn, or auto-correct based on your policies.

◆ Analytics Processor
AnalyticsProcessor

Enrich or aggregate analytics data from custom sources, normalise third-party metrics, or compute derived KPIs.

⊕ Webhook Handler
WebhookHandler

Handle incoming webhooks from platforms or external services and route them into the SocialScale event bus.

+ Custom
Custom(String)

Arbitrary custom capability string for plugins that don't fit the built-in types. Registered with the host registry under your custom identifier.

Getting started

Build, test, and publish your first plugin in 5 steps.

1 Write the manifest

plugin.json manifest
{
  "id": "com.example.my-platform",
  "name": "My Platform Connector",
  "version": "1.0.0",
  "description": "Adds My Platform to SocialScale",
  "author": "Your Name <you@example.com>",
  "capabilities": ["platform_connector"],
  "wasm_path": "./target/wasm32-wasi/release/my_plugin.wasm",
  "config_schema": {
    "type": "object",
    "properties": {
      "api_key": {
        "type": "string",
        "description": "My Platform API key"
      }
    },
    "required": ["api_key"]
  }
}
2 Implement the 5 exports

Your plugin must export: alloc, dealloc, plugin_init, plugin_capabilities, and plugin_handle_request. See the ABI reference below.

3 Compile to WASM
cargo build --target wasm32-wasi --release
# Output: target/wasm32-wasi/release/my_plugin.wasm
4 Install locally
socialscale plugin install ./plugin.json
# Loads the .wasm from the path in the manifest
5 Publish to marketplace
socialscale plugin publish
# Uploads to the registry and assigns a semver tag

ABI Reference

ABI version 1. All data crosses the host–plugin boundary as JSON, encoded in WASM linear memory using ptr+len pairs.

Pointer encoding (host ↔ plugin)
// 64-bit encoding: (ptr << 32) | len
//
// Plugin returns:  i64 = encode_ptr_len(ptr, len)
// Host decodes:    let (ptr, len) = decode_ptr_len(packed);
//
// All data crossing the boundary is JSON, passed through
// WASM linear memory using ptr+len pairs.

↑ Plugin exports (plugin → host)

alloc(size: i32) → i32

Allocate memory in WASM linear memory. Host uses this before writing input data.

dealloc(ptr: i32, size: i32)

Free memory previously allocated by alloc.

plugin_init() → i64

Return plugin metadata (id, name, version, abi_version) as ptr+len encoded JSON.

plugin_capabilities() → i64

Declare what the plugin can do (publish_text, read_analytics, platforms…).

plugin_handle_request(ptr: i32, len: i32) → i64

Main entry point. Receives a JSON-encoded PluginRequest, returns a JSON-encoded PluginResponse.

↓ Host functions (provided to plugin)

host_log(level: i32, ptr: i32, len: i32)

Write a log message at the given level (0=trace … 4=error).

host_http_request(ptr: i32, len: i32) → i64

Make an outbound HTTP request. Domain must be in the allowlist declared in capabilities.

host_store_get(kptr: i32, klen: i32) → i64

Read a value from the plugin KV store by key. Returns ptr+len of the value.

host_store_set(kp,kl,vp,vl: i32)

Write a key-value pair to the plugin KV store.

host_emit_event(ptr: i32, len: i32)

Emit a JSON event on the host event bus for other plugins or agents to consume.

Host module name: "env". Import in Rust with #[link(wasm_import_module = "env")].

Capabilities & Permissions

Plugins declare what they need in their manifest. Users approve on install. The host enforces at runtime — requests to undeclared domains are blocked, KV access requires storage_access, and all actions are audit-logged.

Capability Approval What it grants
network_access Requires approval Outbound HTTP requests. Declare the allowed domains in required_network_domains. Host enforces a per-domain allowlist at runtime.
storage_access Requires approval Read and write values in the plugin-scoped KV store. Keys are namespaced to your plugin ID.
event_emit Requires approval Emit events on the host event bus. Other plugins or agents may subscribe.
credential_access Explicit approval Access OAuth tokens for the platform this plugin serves. Requires explicit user approval at install time.

Rate limiting

Every plugin has a sliding-window rate limit of 1 000 outbound HTTP requests per hour by default. Exceeded requests are rejected with a RateLimitExceeded error. Limits can be configured per-plugin by the administrator.

Request & Response types

All calls to plugin_handle_request carry a tagged union JSON payload with a "type" discriminant.

PluginRequest variants

Publish text, media_urls, hashtags, metadata

Publish content to the platform. Core method for all PlatformConnectors.

DeletePost post_id

Delete a published post by its platform-native ID.

FetchComments post_id, cursor?

Fetch comments on a post. Use cursor for pagination.

ReplyComment comment_id, text

Reply to a specific comment.

FetchInsights start (ISO 8601), end (ISO 8601)

Fetch analytics for a date range.

FetchPostMetrics post_id

Fetch engagement metrics for a single post.

ExchangeCode code, redirect_uri

Exchange an OAuth authorization code for tokens.

RefreshToken refresh_token

Refresh an expired OAuth access token.

Custom action: string, payload: JSON

Arbitrary custom action for plugin-specific behaviour.

Success response

{
  "status": "ok",
  // any additional fields are
  // flattened into the response
  "post_id": "abc123",
  "url": "https://..."
}

Error response

{
  "status": "error",
  "code": "rate_limit_exceeded",
  "message": "Try again in 60s",
  "retryable": true
}
Rust example

Minimal platform connector

A complete skeleton showing all 5 required exports. Compile with cargo build --target wasm32-wasi --release.

src/lib.rs Rust
use serde::{Deserialize, Serialize};
use serde_json::Value;

// Required by the host to allocate/free memory
#[no_mangle]
pub extern "C" fn alloc(size: i32) -> i32 {
    let mut buf = Vec::<u8>::with_capacity(size as usize);
    let ptr = buf.as_mut_ptr() as i32;
    std::mem::forget(buf);
    ptr
}

#[no_mangle]
pub extern "C" fn dealloc(ptr: i32, size: i32) {
    unsafe { drop(Vec::from_raw_parts(ptr as *mut u8, 0, size as usize)) }
}

// Metadata returned to the host
#[no_mangle]
pub extern "C" fn plugin_init() -> i64 {
    let meta = serde_json::json!({
        "abi_version": 1,
        "id": "com.example.my-platform",
        "name": "My Platform Connector",
        "version": "1.0.0",
        "author": "Your Name"
    });
    return_json(&meta)
}

// Declare capabilities
#[no_mangle]
pub extern "C" fn plugin_capabilities() -> i64 {
    let caps = serde_json::json!({
        "abi_version": 1,
        "can_publish_text": true,
        "can_publish_image": true,
        "can_publish_video": false,
        "can_read_analytics": true,
        "can_manage_ads": false,
        "platforms": ["my-platform"],
        "required_network_domains": ["api.my-platform.com"],
        "needs_kv_store": true
    });
    return_json(&caps)
}

// Main request handler
#[no_mangle]
pub extern "C" fn plugin_handle_request(ptr: i32, len: i32) -> i64 {
    let request: Value = read_json(ptr, len);

    let response = match request["type"].as_str().unwrap_or("") {
        "publish" => handle_publish(&request),
        "fetch_insights" => handle_insights(&request),
        "exchange_code" => handle_oauth(&request),
        _ => serde_json::json!({
            "status": "error",
            "code": "unsupported",
            "message": "Request type not supported",
            "retryable": false
        }),
    };

    return_json(&response)
}
Cargo.toml TOML
[package]
name = "my-plugin"
version = "1.0.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]   # ← required for WASM

[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"

# WASM target: cargo build --target wasm32-wasi --release

Security model

Every plugin runs in a fully isolated Wasmtime sandbox. No host memory is accessible; all I/O goes through typed host functions.

⊕ Memory isolation

Plugins have their own linear memory. They cannot read or write host memory directly. All data exchange goes through the alloc/dealloc ABI.

◎ Network allowlist

Every outbound HTTP request is checked against the domain allowlist the plugin declared at install. Requests to unlisted domains are rejected immediately.

◆ Capability gating

Plugins can only use the capabilities they declared and the user approved. Attempting to call host_store_get without storage_access returns an error.

✦ Full audit log

Every capability check, network request, store read/write, and event emit is recorded with a timestamp in the audit log. Viewable in the dashboard.

⬡ Rate limiting

1 000 outbound HTTP requests per hour by default, enforced with a sliding-window algorithm. Exceeding the limit returns a retryable error.

⊞ User approval

No plugin runs until the user has explicitly approved its capabilities. Capability re-requests after install require a new approval prompt.

Build your first plugin

Install the CLI, scaffold a plugin, and have it running locally in under 10 minutes.

Questions? hello@socialscale.zuhabul.com