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