MAP Docs

Plugins

Three tiers of protocol extension. Manifest format, signing, hot-load lifecycle.

MAP's three-tier extensibility model lets the institution add new protocols without recompiling. Three tiers, ordered by performance and trust.

TierMechanismLatency overheadTrust level
Tier 1: NativeCompiled Rust crate implementing ProtocolModuleNoneFirst-party (compiled into engine)
Tier 2: WASIWasmtime 37 component-model plugin loaded at runtimeModerate (sandboxed)Third-party with signature verification
Tier 3: External HTTPRemote process implementing the MAP invoke contractHigher (network hop)Treaty-bound or local

Tier 1 — Native

The reference path. A native protocol is a Rust crate in protocols/<name>-lib/ that implements:

#[async_trait]
impl ProtocolModule for MyProtocol {
    fn protocol_name(&self) -> &'static str { "MYPROTOCOL" }
    fn version(&self) -> &'static str { "v1.0.0" }
    fn operations(&self) -> Vec<&'static str> { vec!["my_op"] }
    async fn invoke(&self, op: &str, payload: Value, ctx: &InvokeContext)
        -> Result<Response, ProtocolError> { /* ... */ }
}

The crate is registered in the engine's static module table at startup. Latency is minimal — no IPC, no serialization across boundaries.

Use Tier 1 for first-party protocols that ship with the engine binary.

Tier 2 — WASI

A WASI plugin is a .wasm file built against the MAP plugin ABI. The engine loads it through Wasmtime 37 (component model + WASIP2). The plugin runs in a sandboxed memory space; it can only call the host functions exposed by the engine.

Manifest

Every WASI plugin ships with a plugin.toml:

[plugin]
name = "myorg-myprotocol"
version = "0.1.0"
description = "Custom analytics protocol for Acme"
plugin_type = "Protocol"  # or "Gateway", "Extension"
protocol = "MYPROTOCOL"   # which MAP protocol slot this implements

[plugin.binary]
source_type = "wasm"
path = "./myorg_myprotocol.wasm"

[plugin.metadata]
author = "did:oas:acme:agent:0x..."
license = "Commercial"
min_map_version = "0.6.0"

[plugin.operations]
operations = ["analyze", "summarize", "report"]

[plugin.integrations]
# Optional LLM / DB / vector store dependencies the plugin needs
llm = { kind = "openai-compat", endpoint = "https://api.openai.com/v1" }

Loading

Plugins are discovered at startup by scanning a configured directory:

# map-engine config
[plugins]
directory = "/var/lib/map/plugins"
auto_discover = true
allowed_sources = ["wasm", "builtin"]
watch_interval_ms = 5000
require_signatures = true
trusted_publishers = [
    "did:oas:l1fe:agent:0x...",
    "did:oas:acme:agent:0x..."
]

When auto_discover = true, the engine scans directory every watch_interval_ms for new manifests. New plugins enter PluginState::Pending, then Registered after the engine validates the manifest, verifies the signature, and successfully loads the WASM.

State machine

Pending → Registered → (Disabled | Error)
StateMeaning
PendingDiscovered; not yet loaded
RegisteredLoaded; serving requests
DisabledDeliberately turned off via admin command
ErrorLoad or runtime failure; reason recorded

The engine exposes MarsAdmin::plugin_status(name) for ops queries.

Signing

If require_signatures = true, every plugin manifest must carry an Ed25519 signature from one of the trusted_publishers. The engine verifies the signature against the published OAS DID document before loading.

[plugin.signature]
signed_by = "did:oas:acme:agent:0x..."
signature = "ed25519:0x..."
signed_at = "2026-05-01T00:00:00Z"

Unsigned or invalidly-signed manifests stay in Pending indefinitely and emit a PluginRejected audit event.

Building a WASI plugin

# In a Rust crate using plugin-sdk:
cargo build --release --target wasm32-wasip2

# Bundle for distribution
cp target/wasm32-wasip2/release/my_plugin.wasm dist/
cp plugin.toml dist/
mv dist my-plugin-v0.1.0/
tar czf my-plugin-v0.1.0.tar.gz my-plugin-v0.1.0/

Deploy by dropping the unpacked plugin directory into /var/lib/map/plugins/.

Tier 3 — External HTTP

For any service implementing the MAP invoke contract over HTTP. Useful when the protocol is written in a language without a WASI target, or when the protocol calls heavyweight backends (large GPUs, proprietary engines).

The contract

#[async_trait]
pub trait RemoteProtocolAdapter: Send + Sync {
    async fn invoke(&self, req: RemoteProtocolRequest)
        -> Result<RemoteProtocolResponse, RemoteAdapterError>;

    async fn health_check(&self) -> Result<RemoteHealthStatus, RemoteAdapterError>;
}

The external service implements:

POST /invoke
GET  /health

Request:

POST /invoke HTTP/2
Content-Type: application/json
X-MAP-Protocol: MYPROTOCOL
X-MAP-Operation: analyze
X-MAP-Correlation-Id: ...

{
  "operation": "analyze",
  "payload":   { ... },
  "ctx":       { /* serialized InvokeContext */ }
}

Response (success):

HTTP/2 200 OK
Content-Type: application/json

{
  "data": { ... },
  "metadata": { ... }
}

Response (error):

HTTP/2 422 Unprocessable Entity

{
  "error": {
    "code": "invalid_payload",
    "message": "field 'subject' is required"
  }
}

Health:

GET /health → 200 OK
{
  "status": "healthy",
  "version": "1.0.0",
  "uptime_seconds": 12345
}

Registration

External adapters are registered explicitly in engine config:

[[external_protocols]]
name      = "MYPROTOCOL"
version   = "v1.0.0"
endpoint  = "https://my-protocol.svc.cluster.local:8080"
timeout   = "30s"
operations = ["analyze", "summarize"]
auth      = { kind = "did-auth", did = "did:oas:l1fe:service:my-protocol-0x..." }

The engine treats it like any other protocol — dispatches through the standard pipeline, audits to MAX, meters via MEAL.

Health & circuit breaker

Health probes run every 10s. Three consecutive failures open the circuit breaker for the endpoint; new requests fail fast with CoreError::CircuitOpen until probes recover.

Tier comparison

PropertyTier 1 (Native)Tier 2 (WASI)Tier 3 (External)
Build artifactCargo crate.wasm + manifestAny HTTP service
LanguageRust onlyAnything compiled to WASIP2Any
Latency overheadNone~50µs (sandbox call)Network RTT
SandboxNone (compile-time safety)Wasmtime memory sandboxProcess/network isolation
Hot-loadRestart onlyYes (with watch_interval)Yes (no restart needed)
Trust requirementCompiled inSigned manifestDID-Auth or treaty
Audit through MAXYesYesYes
Meter through MEALYesYesYes
State persistenceEngine's databaseEngine-mediated KVService's own database

Most third-party protocols start at Tier 3 (HTTP) for fast iteration, migrate to Tier 2 (WASI) once stable for tighter sandbox + lower latency, and may be absorbed to Tier 1 (native) if the L1fe core picks them up.

See also

On this page