Plugin System

MockForge features a powerful WebAssembly-based plugin system that allows you to extend functionality without modifying the core framework. Plugins run in a secure sandbox with resource limits and provide capabilities for custom response generation, authentication, data sources, and template extensions.

Overview

The plugin system enables:

  • Custom Response Generators: Create specialized mock data and responses
  • Authentication Providers: Implement JWT, OAuth2, and custom authentication schemes
  • Data Source Connectors: Connect to CSV files, databases, and external APIs
  • Template Extensions: Add custom template functions and filters
  • Protocol Handlers: Extend support for custom protocols and formats

Plugin Architecture

WebAssembly Runtime

Plugins are compiled to WebAssembly (WASM) and run in an isolated runtime environment:

  • Security Sandbox: Isolated execution prevents plugins from accessing unauthorized resources
  • Resource Limits: CPU, memory, and execution time constraints
  • Capability System: Fine-grained permissions control what plugins can access
  • Cross-platform: WASM plugins work on any platform MockForge supports

Plugin Types

MockForge supports several plugin types:

TypeDescriptionInterface
responseGenerate custom response dataResponseGenerator
authHandle authentication and authorizationAuthProvider
datasourceConnect to external data sourcesDataSourceConnector
templateAdd custom template functionsTemplateExtension
protocolSupport custom protocolsProtocolHandler

Installing Plugins

From Plugin Registry

# Install plugin from registry
mockforge plugin install auth-jwt

# Install specific version
mockforge plugin install [email protected]

# Search the registry
mockforge plugin search auth

From Local File

# Install from local WASM file
mockforge plugin install ./my-plugin.wasm

# Install from a local plugin directory (reads plugin.yaml from the directory)
mockforge plugin install ./my-plugin/

# Unsigned plugin you built yourself (see "Verification & Trust")
mockforge plugin install ./my-plugin/ --allow-unsigned

From Git Repository

# Install from Git repository
mockforge plugin install https://github.com/example/mockforge-plugin-custom.git

# Install specific branch/tag
mockforge plugin install https://github.com/example/mockforge-plugin-custom.git#v1.0.0

Plugin Management

List Installed Plugins

# List all installed plugins
mockforge plugin list

# Show detailed information
mockforge plugin list --detailed

# Show details for one plugin
mockforge plugin info auth-jwt

Update Plugins

# Update specific plugin
mockforge plugin update auth-jwt

# Update all plugins
mockforge plugin update --all

Remove Plugins

# Remove plugin
mockforge plugin uninstall auth-jwt

Plugin Configuration

Global Configuration

Configure plugins in your MockForge configuration file:

plugins:
  enabled: true
  directory: "~/.mockforge/plugins"
  runtime:
    memory_limit_mb: 64
    cpu_limit_percent: 10
    execution_timeout_ms: 5000
  
  # Plugin-specific configuration
  auth-jwt:
    enabled: true
    config:
      secret_key: "${JWT_SECRET}"
      algorithm: "HS256"
      expiration: 3600
  
  datasource-csv:
    enabled: true
    config:
      base_directory: "./data"
      cache_ttl: 300

Environment Variables

Plugin system settings (registry URL, runtime limits, directory) are YAML-only — configure under plugins.* in mockforge.yaml. The token for publishing to the registry is the only env var:

export MOCKFORGE_REGISTRY_TOKEN=mfreg_...

# Plugin-specific settings (per-plugin auth/data, NOT MockForge core)
export JWT_SECRET=your-secret-key
export CSV_DATA_DIR=./test-data

Developing Plugins

Plugin Manifest

Every plugin requires a plugin.yaml manifest file:

# plugin.yaml
name: "auth-jwt"
version: "1.0.0"
description: "JWT authentication provider"
author: "Your Name <[email protected]>"
license: "MIT"
repository: "https://github.com/example/mockforge-plugin-auth-jwt"

# Plugin metadata
type: "auth"
category: "authentication"
tags: ["jwt", "auth", "security"]

# Runtime requirements
runtime:
  wasm_version: "0.1"
  memory_limit_mb: 32
  execution_timeout_ms: 1000

# Capabilities required
capabilities:
  - "network.http.client"
  - "storage.key_value"
  - "template.functions"

# Configuration schema
config_schema:
  type: "object"
  properties:
    secret_key:
      type: "string"
      description: "JWT signing secret"
      required: true
    algorithm:
      type: "string"
      enum: ["HS256", "HS384", "HS512", "RS256"]
      default: "HS256"
    expiration:
      type: "integer"
      description: "Token expiration in seconds"
      default: 3600
      minimum: 60

# Export information
exports:
  auth_provider: "JwtAuthProvider"
  template_functions:
    - "jwt_encode"
    - "jwt_decode"
    - "jwt_verify"

Rust Plugin Development

Create a new Rust project for your plugin:

cargo new --lib mockforge-plugin-custom
cd mockforge-plugin-custom

Add dependencies to Cargo.toml:

[package]
name = "mockforge-plugin-custom"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]
mockforge-plugin-core = "0.1.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
wasm-bindgen = "0.2"

[dependencies.web-sys]
version = "0.3"
features = [
  "console",
]

Implement your plugin in src/lib.rs:

#![allow(unused)]
fn main() {
use mockforge_plugin_core::{
    AuthProvider, AuthResult, PluginConfig, PluginError, PluginResult,
    export_auth_provider, export_template_functions
};
use serde::{Deserialize, Serialize};
use wasm_bindgen::prelude::*;

#[derive(Deserialize)]
struct JwtConfig {
    secret_key: String,
    algorithm: String,
    expiration: u64,
}

pub struct JwtAuthProvider {
    config: JwtConfig,
}

impl JwtAuthProvider {
    pub fn new(config: PluginConfig) -> PluginResult<Self> {
        let jwt_config: JwtConfig = serde_json::from_value(config.into())?;
        Ok(Self { config: jwt_config })
    }
}

impl AuthProvider for JwtAuthProvider {
    fn authenticate(&self, token: &str) -> PluginResult<AuthResult> {
        // Implement JWT validation logic
        match self.verify_jwt(token) {
            Ok(claims) => Ok(AuthResult::success(claims)),
            Err(e) => Ok(AuthResult::failure(e.to_string())),
        }
    }
    
    fn generate_token(&self, user_id: &str) -> PluginResult<String> {
        // Implement JWT generation logic
        self.create_jwt(user_id)
    }
}

impl JwtAuthProvider {
    fn verify_jwt(&self, token: &str) -> Result<serde_json::Value, PluginError> {
        // JWT verification implementation
        todo!("Implement JWT verification")
    }
    
    fn create_jwt(&self, user_id: &str) -> PluginResult<String> {
        // JWT creation implementation
        todo!("Implement JWT creation")
    }
}

// Template functions
#[wasm_bindgen]
pub fn jwt_encode(payload: &str, secret: &str) -> String {
    // Implement JWT encoding for templates
    todo!("Implement template JWT encoding")
}

#[wasm_bindgen]
pub fn jwt_decode(token: &str) -> String {
    // Implement JWT decoding for templates
    todo!("Implement template JWT decoding")
}

// Export plugin interfaces
export_auth_provider!(JwtAuthProvider);
export_template_functions! {
    "jwt_encode" => jwt_encode,
    "jwt_decode" => jwt_decode,
}
}

Building Plugins

Build your plugin to WebAssembly:

# Install wasm-pack if not already installed
cargo install wasm-pack

# Build the plugin
wasm-pack build --target web --out-dir pkg

# The WASM file will be in pkg/mockforge_plugin_custom.wasm

Testing Plugins

MockForge provides a testing framework for plugins:

#![allow(unused)]
fn main() {
#[cfg(test)]
mod tests {
    use super::*;
    use mockforge_plugin_core::test_utils::*;

    #[test]
    fn test_jwt_authentication() {
        let config = test_config! {
            "secret_key": "test-secret",
            "algorithm": "HS256",
            "expiration": 3600
        };
        
        let provider = JwtAuthProvider::new(config).unwrap();
        
        // Test valid token
        let token = provider.generate_token("user123").unwrap();
        let result = provider.authenticate(&token).unwrap();
        assert!(result.is_success());
        
        // Test invalid token
        let invalid_result = provider.authenticate("invalid.token.here").unwrap();
        assert!(invalid_result.is_failure());
    }
}
}

Plugin Examples

MockForge includes several example plugins to demonstrate different capabilities:

Authentication Plugins

Basic Authentication (auth-basic)

# examples/plugins/auth-basic/plugin.yaml
name: "auth-basic"
type: "auth"
description: "HTTP Basic Authentication provider"

config_schema:
  type: "object"
  properties:
    users:
      type: "object"
      description: "Username to password mapping"
    realm:
      type: "string"
      default: "MockForge"

Usage in MockForge configuration:

plugins:
  auth-basic:
    enabled: true
    config:
      realm: "API Access"
      users:
        admin: "password123"
        user: "userpass"

JWT Authentication (auth-jwt)

Advanced JWT authentication with support for multiple algorithms:

# examples/plugins/auth-jwt/plugin.yaml
name: "auth-jwt"
type: "auth"
description: "JWT authentication provider with multiple algorithm support"

capabilities:
  - "storage.key_value"
  - "template.functions"

config_schema:
  type: "object"
  properties:
    secret_key:
      type: "string"
      required: true
    algorithm:
      type: "string"
      enum: ["HS256", "HS384", "HS512", "RS256", "RS384", "RS512"]
      default: "HS256"
    issuer:
      type: "string"
      description: "JWT issuer claim"
    audience:
      type: "string"
      description: "JWT audience claim"

Data Source Plugins

CSV Data Source (datasource-csv)

Connect to CSV files as data sources:

# examples/plugins/datasource-csv/plugin.yaml
name: "datasource-csv"
type: "datasource"
description: "CSV file data source connector"

config_schema:
  type: "object"
  properties:
    base_directory:
      type: "string"
      description: "Base directory for CSV files"
      required: true
    cache_ttl:
      type: "integer"
      description: "Cache TTL in seconds"
      default: 300
    delimiter:
      type: "string"
      description: "CSV delimiter"
      default: ","

Usage in templates:

response:
  status: 200
  body:
    users: "{{datasource.csv('users.csv').random(5)}}"
    products: "{{datasource.csv('products.csv').filter('category', 'electronics')}}"

Template Plugins

Crypto Functions (template-crypto)

Add cryptographic template functions:

# examples/plugins/template-crypto/plugin.yaml
name: "template-crypto"
type: "template"
description: "Cryptographic template functions"

exports:
  template_functions:
    - "crypto_hash"
    - "crypto_hmac"
    - "crypto_encrypt"
    - "crypto_decrypt"
    - "crypto_random"

Template usage:

response:
  body:
    user_id: "{{uuid}}"
    password_hash: "{{crypto_hash(faker.password, 'sha256')}}"
    api_key: "{{crypto_random(32, 'hex')}}"
    signature: "{{crypto_hmac(request.body, env.API_SECRET, 'sha256')}}"

Response Plugins

GraphQL Response Generator (response-graphql)

Generate GraphQL responses from schema:

# examples/plugins/response-graphql/plugin.yaml
name: "response-graphql"
type: "response"
description: "GraphQL response generator"

config_schema:
  type: "object"
  properties:
    schema_file:
      type: "string"
      description: "Path to GraphQL schema file"
      required: true
    resolvers:
      type: "object"
      description: "Custom resolver configuration"

Security Considerations

Capability System

Plugins must declare required capabilities:

# plugin.yaml
capabilities:
  - "network.http.client"     # Make HTTP requests
  - "network.http.server"     # Handle HTTP requests
  - "storage.key_value"       # Access key-value storage
  - "storage.file.read"       # Read files
  - "storage.file.write"      # Write files
  - "template.functions"      # Register template functions
  - "crypto.random"           # Access random number generation
  - "crypto.hash"             # Access hashing functions

Resource Limits

Configure resource limits per plugin:

plugins:
  my-plugin:
    runtime:
      memory_limit_mb: 64        # Maximum memory usage
      cpu_limit_percent: 5       # Maximum CPU usage
      execution_timeout_ms: 2000 # Maximum execution time
      network_timeout_ms: 1000   # Network request timeout

Sandboxing

Plugins run in a secure sandbox that:

  • Prevents access to the host file system outside permitted directories
  • Limits network access to declared endpoints
  • Restricts system calls and resource usage
  • Isolates plugin memory from the host process

Best Practices

Plugin Development

  1. Keep plugins focused: Each plugin should have a single, clear purpose
  2. Minimize resource usage: Use efficient algorithms and limit memory allocation
  3. Handle errors gracefully: Return meaningful error messages
  4. Document configuration: Provide clear schema and examples
  5. Test thoroughly: Include comprehensive tests for all functionality

Plugin Usage

  1. Review plugin capabilities: Understand what permissions plugins require
  2. Monitor resource usage: Check plugin performance and resource consumption
  3. Keep plugins updated: Regularly update to get security fixes and improvements
  4. Use official plugins: Prefer plugins from trusted sources
  5. Test in development: Thoroughly test plugins before production use

Security

  1. Audit plugin code: Review plugin source code when possible
  2. Limit capabilities: Only grant necessary permissions
  3. Monitor logs: Watch for suspicious plugin behavior
  4. Use resource limits: Prevent plugins from consuming excessive resources
  5. Isolate environments: Use separate plugin configurations for development and production

Hook Points Reference

MockForge plugins implement one or more of seven traits, each hooked into a specific point in the request / response lifecycle. A single plugin can implement several traits to extend multiple stages.

TraitWhen it runsUse cases
TemplatePluginDuring response templatingCustom {{my.fn arg}} template functions, inline crypto / encoding helpers
AuthPluginBefore route matchingCustom JWT/OAuth validators, header-based auth schemes, request signing verification
ResponsePluginAfter route matching, before writeGenerate the response body from scratch (e.g. AI-driven, computed)
ResponseModifierPluginAfter response generationPost-process bodies (compression, redaction, watermarking, schema masking)
DataSourcePluginWhen a route requests external dataCSV / database / external-API connectors backing fixtures
BackendGeneratorPluginAt spec import timeGenerate full mock backends from non-OpenAPI specs (gRPC reflect, custom DSLs)
ClientGeneratorPluginAt spec import timeEmit client SDKs from a spec for testing

Each trait gets a PluginContext with:

  • The current request (method, path, headers, body)
  • A handle to the in-memory storage (storage.key_value capability)
  • A logger (tracing macros, scoped to the plugin name)
  • Random / time / template helpers from the host

See crates/mockforge-plugin-core/src/ for the canonical trait definitions.

Publishing to the Registry

The plugin registry at registry.mockforge.dev hosts community plugins, checksummed and optionally signed, with semver-pinnable versions. Authoring and publishing commands live in the separate mockforge-plugin binary. Publishing is a four-step flow.

1. Generate publisher keys

# One-time setup: writes the private key (PKCS#8 PEM, mode 0600) and prints the base64 public key
mockforge-plugin key gen --out mockforge_publisher_key.pem

# Register the public key with your registry account
mockforge-plugin key add --label laptop --public-key <base64-public-key>

# List the keys registered on your account
mockforge-plugin key list

Your private key signs the SBOM attestation for each release; the registry verifies it against your registered public keys. Keep the private key file readable only by your user account. Use mockforge-plugin key rotate to replace a key and mockforge-plugin key revoke <id> to retire one.

2. Authenticate to the registry

# Create an account at https://registry.mockforge.dev and copy your token
export MOCKFORGE_REGISTRY_TOKEN=mfreg_...

All mockforge-plugin key and publish commands also accept --token and --registry (or MOCKFORGE_REGISTRY_URL).

3. Prepare the manifest

# plugin.yaml in your plugin's repo root
name: "my-cool-plugin"
version: "1.0.0"          # semver, monotonically increasing per release
description: "A short, scannable summary"
author: "your-name"
license: "MIT OR Apache-2.0"
homepage: "https://github.com/you/my-cool-plugin"
repository: "https://github.com/you/my-cool-plugin"

capabilities:
  - "network.http.client"
  - "storage.key_value"

runtime:
  memory_limit_mb: 64
  cpu_limit_percent: 5
  execution_timeout_ms: 1000

# Versions of MockForge this plugin targets
mockforge:
  min_version: "0.3.125"
  max_version: "0.4.0"

# Files to include in the published bundle (relative to repo root)
include:
  - "target/wasm32-unknown-unknown/release/my_cool_plugin.wasm"
  - "plugin.yaml"
  - "README.md"
  - "LICENSE-*"

Validate before pushing (run from the plugin project directory):

mockforge-plugin validate

4. Publish

# Build the WASM module, package it as a .zip, then publish
mockforge-plugin build --release
mockforge-plugin package
mockforge-plugin publish

# Attach a signed SBOM attestation
mockforge-plugin publish --sign --key-file mockforge_publisher_key.pem --sbom sbom.json

# Validate and describe the upload without sending it
mockforge-plugin publish --dry-run

Under the hood:

  1. The .zip package produced by package is located and validated (it must contain the manifest and a .wasm module)
  2. With --sign, the SBOM is signed over SHA-256(artifact_checksum || canonical(sbom)) with your private key
  3. The package (plus SBOM and signature, if signed) is uploaded with the bearer token from --token or MOCKFORGE_REGISTRY_TOKEN
  4. The registry verifies the signature against your registered public keys

Yanking a release

If a release has a critical bug, yank it. There is no CLI command for this; call the registry API with your token:

curl -X DELETE \
  -H "Authorization: Bearer $MOCKFORGE_REGISTRY_TOKEN" \
  https://registry.mockforge.dev/api/v1/plugins/my-cool-plugin/versions/1.0.0/yank

Yanks aren’t deletions — they’re advisory. To fully remove a release for GDPR / legal reasons, contact registry operators.

Verification & Trust

Every mockforge plugin install and mockforge plugin update verifies the plugin’s signature before it validates or loads anything, whatever the source (registry, URL, Git or local path):

  1. For registry and URL sources, the SHA-256 checksum must match the registry’s value or --checksum.
  2. The plugin directory must contain a plugin.sig in signature format v2. It covers every file in the plugin (manifest, WASM module and any other files), so a modified, added or swapped file fails verification. So does a signature copied from a different plugin.
  3. The signing key must be in your trust store and must not be revoked or past its expires_at.
  4. The signature must verify against that key’s public key.

If any step fails, the install aborts and nothing is loaded. --skip-validation does not skip signature verification.

Trust store

The CLI reads trusted publisher keys from the file given with --trusted-keys FILE, else from $MOCKFORGE_PLUGIN_TRUSTED_KEYS_FILE, else from ~/.mockforge/trusted-keys.json if it exists:

{
  "keys": [
    { "key_id": "acme-2026", "public_key": "<hex Ed25519 public key>",
      "expires_at": "2027-01-01T00:00:00Z" },
    { "key_id": "acme-2025", "public_key": "<hex>", "revoked": true }
  ]
}

A revoked entry blocks that key even if another file trusts it. Signatures from an older format (v1, which covered only plugin.toml) are rejected; re-sign the plugin.

Unsigned development plugins

A plugin without plugin.sig is refused by default. For a local plugin you built yourself, opt out explicitly:

mockforge plugin install ./my-plugin/ --allow-unsigned

The CLI prints a warning every time. --allow-unsigned only covers a missing signature: a plugin whose plugin.sig is present but invalid, untrusted, expired or revoked is still rejected. (--no-verify is accepted as an alias of --allow-unsigned. It no longer skips verification of a signature that is present.)

Admin UI installation

The admin installer verifies the plugin before validation or loading, using MOCKFORGE_PLUGIN_TRUSTED_KEYS_FILE or ~/.mockforge/trusted-keys.json on the server. Configure publisher trust on the server; the install request cannot supply trusted keys.

Unsigned plugins are rejected by default. For a plugin you built yourself, select Allow unsigned plugin for local development in the install dialog. The admin API exposes the same explicit allow_unsigned: true option. A malformed, modified, untrusted, revoked or expired signature is rejected even when unsigned plugins are allowed. skip_validation and no_verify are still rejected by the admin endpoint. Failed verification leaves an existing plugin registered, including when force was requested.

Verifying a third-party plugin manually

# Validate a plugin without installing it
mockforge plugin validate ./suspicious-plugin/

# Install from a URL, pinning the expected SHA-256 checksum
mockforge plugin install https://example.com/plugin.zip --checksum <sha256>

Always validate plugins from unfamiliar publishers before installing.

Troubleshooting

Common Issues

Plugin Won’t Load

# Show plugin information
mockforge plugin info my-plugin

# Validate the plugin
mockforge plugin validate ./my-plugin/

# Check logs for errors
mockforge logs --json | grep -i plugin

Runtime Errors

# Enable debug logging
RUST_LOG=mockforge_plugin_loader=debug mockforge serve

Performance Issues

There is no CLI for per-plugin runtime stats or profiling. Use debug logging (above) and mockforge plugin cache-stats to inspect the plugin download cache.

This comprehensive plugin system enables powerful extensibility while maintaining security and performance. Plugins can significantly extend MockForge’s capabilities for specialized use cases and integrations.