Skip to content

MFTPlus Plugin API ​

This reference documents the actual plugin surface shipped in the product: the Rust mft_plugin SDK crate, the host functions the runtime links into every plugin (WebAssembly module "mft"), and the transfer-event hooks the loader invokes. Nothing here is invented — every symbol maps to a definition in the plugin runtime source.

Architecture at a glance ​

  • A plugin is a WASM module. The runtime (wasmtime) instantiates it and links the mft host functions.
  • Only imports from module "mft" and "wasi_snapshot_preview1" are permitted; all other imports are rejected.
  • The plugin exports hook functions by name; the loader calls each hook when the corresponding transfer event occurs. Missing hooks are treated as no-ops.
  • Privileged operations (network, file, clipboard) are gated by the plugin's declared permissions and enforced deny-by-default.

Host functions ​

These are the functions a plugin may import from module "mft". Signatures are taken verbatim from crates/mft-plugin-runtime/src/host.rs (and the app linker in src/plugins/wasm.rs).

Host functionImportSignatureStatus
Logmft.log(param i32 ptr, i32 len) -> i32Callable; writes the UTF-8 message at ptr..ptr+len to the agent log.
HTTP requestmft.http_request(param i32 url_ptr, i32 url_len, i32 body_ptr, i32 body_len) -> i32Stub in current source (returns 1); network calls are not yet wired.
Transfer infomft.get_transfer_info(param i32 transfer_id_ptr, i32 transfer_id_len) -> i32Stub in current source (returns 0).
Timestampmft.get_timestamp(param) -> i32Stub in current source (returns 1).

Return value convention: 0 indicates success; non-zero indicates a host-side failure or "not implemented" for the stubbed functions.

Strings are passed by pointer + length into the plugin's exported linear memory. The host reads memory.data at the given offset; the plugin must keep the bytes live for the duration of the call.

Example: calling mft.log from WAT ​

wat
(module
  (import "mft" "log" (func $log (param i32 i32) (result i32)))
  (memory (export "memory") 1)
  (data (i32.const 0) "plugin booted\00")
  (func (export "on_init") (result i32)
    i32.const 0
    i32.const 13
    call $log
    i32.const 0
  )
)

Transfer-event hooks ​

The loader calls the following exported functions by name. The high-level SDK presents them as the Plugin trait methods listed below; the runtime invokes the raw WASM exports.

Hook (WASM export)SDK trait methodCalled when
on_initPlugin::on_initPlugin is first loaded.
on_transfer_startPlugin::on_transfer_startA transfer starts.
on_transfer_progressPlugin::on_transfer_progressTransfer progress updates (receives an f64 progress).
on_transfer_completePlugin::on_transfer_completeA transfer completes successfully.
on_transfer_failedPlugin::on_transfer_failedA transfer fails (receives an error &str).
on_shutdownPlugin::on_shutdownPlugin is unloaded.

Current loader behavior: on_init, on_transfer_start, on_transfer_complete, and on_shutdown are called with no arguments; on_transfer_progress receives an f64; on_transfer_failed's string argument is currently skipped (a documented TODO in the runtime). Hooks may be omitted — the loader treats a missing export as a successful no-op.

The mft_plugin SDK crate ​

Plugins written in Rust depend on the mft_plugin crate. Its public surface (crates/mft-plugin/src/lib.rs):

rust
pub use manifest::{PluginManifest, PluginMetadata, Permissions, RuntimeConfig};
pub use permissions::{PermissionSet, PermissionViolation, ResourceLimits};
pub use types::{TransferInfo, TransferStatus, HookContext, HookType};

Plugin trait ​

Implemented by every plugin. All methods have empty default bodies; override only what you need.

rust
pub trait Plugin {
    fn on_init(&mut self, ctx: &HookContext) -> Result<()>;
    fn on_transfer_start(&mut self, ctx: &HookContext, transfer: &TransferInfo) -> Result<()>;
    fn on_transfer_progress(&mut self, ctx: &HookContext, transfer: &TransferInfo, progress: f64) -> Result<()>;
    fn on_transfer_complete(&mut self, ctx: &HookContext, transfer: &TransferInfo) -> Result<()>;
    fn on_transfer_failed(&mut self, ctx: &HookContext, transfer: &TransferInfo, error: &str) -> Result<()>;
    fn on_shutdown(&mut self, ctx: &HookContext) -> Result<()>;
}

The SDK bridges your Plugin implementation to the WASM hook exports (on_init, …) at build time; you do not export the symbols manually when using the trait.

HookContext ​

Passed to every hook.

FieldTypeNotes
plugin_idStringThis plugin's id (<name>@<version>).
transfer_idOption<String>Set for transfer hooks.
hook_typeHookTypeWhich hook is firing.
invoked_atu64Unix timestamp (seconds) when the hook was invoked.
transferOption<TransferInfo>Snapshot of the transfer for transfer hooks.

HookType ​

Init, TransferStart, TransferProgress, TransferComplete, TransferFailed, Shutdown (serde snake_case: init, transfer_start, …).

TransferInfo ​

FieldTypeNotes
idStringUnique transfer id.
statusTransferStatusCurrent status.
sourceStringSource path/URL.
destinationStringDestination path.
total_bytesu64Total size.
transferred_bytesu64Bytes transferred so far.
started_atOption<u64>Unix timestamp.
completed_atOption<u64>Unix timestamp.
errorOption<String>Present when status == Failed.
metadataHashMap<String, serde_json::Value>Additional metadata (flattened).

TransferInfo also provides progress() -> f64, bytes_per_second() -> Option<u64>, and eta_seconds() -> Option<u64>.

TransferStatus ​

queued, running, completed, failed, cancelled, paused (serde lowercase). *_is_terminal() is true for completed/failed/cancelled; is_active() is true for running/paused.

Permissions (manifest) ​

Used in mft-plugin.toml under [permissions]:

FieldTypeNotes
networkVec<String>Domain glob allowlist.
file_readVec<String>Path glob patterns.
file_writeVec<String>Path glob patterns.
transfer_eventsVec<String>Subscribed event names.
clipboardboolClipboard access opt-in.

ResourceLimits (manifest) ​

Under [limits]; defaults shown:

FieldTypeDefault
max_memory_mbusize128
max_cpu_percentusize10
max_network_requests_per_minuteu3260
max_execution_time_secondsu6430

PermissionSet and PermissionViolation ​

At runtime the manifest Permissions are compiled into a PermissionSet with allows_network(url), allows_file_read(path), allows_file_write(path), allows_transfer_event(event), and allows_clipboard(). A violation is reported as a PermissionViolation:

  • network_denied { url }
  • file_read_denied { path }
  • file_write_denied { path }
  • clipboard_denied
  • resource_limit_exceeded { resource, limit }

Signing & verification — current status ​

IMPORTANT

The current product does NOT perform signature verification on plugins. There is no key-generation or verification step in the loader, the install path, or the manifest parser. The only reference to plugin signing in the source tree is ARCHITECTURE.md, which lists "Plugin Signing: Signature verification for trust" as a future enhancement (not yet implemented). Transfer encryption uses ed25519/x25519 key agreement, but that is unrelated to plugin trust.

Therefore: do not ship documentation or tooling that tells users to generate ed25519 keys or sign plugins — there is nowhere for a signature to be checked today. When signing lands, the manifest will gain a signature field and the loader will verify it before on_init.

Security model summary ​

  • WASM sandbox: memory isolation, no direct OS access.
  • Import validation: only mft and wasi_snapshot_preview1 imports are allowed.
  • Fuel metering: CPU consumption is bounded via wasmtime fuel.
  • Memory caps: per-plugin heap size is constrained.
  • Permission enforcement: deny-by-default network/file/clipboard, with glob allowlists.
  • Rate limiting: network requests are throttled per max_network_requests_per_minute.

Next steps ​