# How to Extend Immich Functionality Using the Plugin System: A WASM Development Guide

> Learn how to extend Immich functionality using WASM plugins. Compile WebAssembly modules, define filters and actions in manifest.json, and let PluginService load your custom code easily.

- Repository: [Immich/immich](https://github.com/immich-app/immich)
- Tags: how-to-guide
- Published: 2026-02-27

---

**Developers extend Immich functionality by compiling WebAssembly modules that expose filters and actions, describing them in a [`manifest.json`](https://github.com/immich-app/immich/blob/main/manifest.json) file, and placing them in the configured plugin directory where `PluginService` automatically loads and executes them via the Extism runtime.**

Immich supports custom functionality through a secure WebAssembly plugin system defined in [`server/src/services/plugin.service.ts`](https://github.com/immich-app/immich/blob/main/server/src/services/plugin.service.ts). Developers write sandboxed WASM modules in languages like Rust or Go, define their capabilities in a manifest file, and register them as workflow filters or actions. This guide covers the complete architecture, from manifest validation to host function integration, based on the immich-app/immich source code.

## Understanding the Immich Plugin Architecture

The plugin system follows a five-stage bootstrap process orchestrated by `PluginService.onBootstrap`. First, the service scans `resourcePaths.corePlugin` and external install folders for [`manifest.json`](https://github.com/immich-app/immich/blob/main/manifest.json) files. Then `readAndValidateManifest` parses each manifest into a `PluginManifestDto` and validates it using class-validator.

Valid plugins are up-serted to the database via `loadPluginToDatabase`, including their filters and actions. Finally, `loadPlugins` creates Extism plugin instances for each compiled `.wasm` file, injecting host functions that bridge WASM code to Immich's API. When workflow triggers fire, `handleTrigger` queues jobs that execute the WASM functions with proper context and authentication.

## Building a WASM Plugin for Immich

### Step 1: Compile the WebAssembly Module

Create a Rust project (or use Go/AssemblyScript) and export functions matching Immich's expected signatures. For example, a filter that processes assets:

```rust
use extism::CurrentPlugin;

#[no_mangle]
pub extern "C" fn filterAsset(cp: CurrentPlugin, offset: i64) -> i32 {
    let result = r#"{"passed": true}"#;
    cp.set_result(offset, result);
    0
}

```

Compile this to `my-plugin.wasm`. The function name `filterAsset` must match the manifest definition.

### Step 2: Create the Plugin Manifest

Define a [`manifest.json`](https://github.com/immich-app/immich/blob/main/manifest.json) that declares the plugin metadata, filters, actions, and required permissions:

```json
{
  "name": "my-plugin",
  "title": "My Sample Plugin",
  "description": "Demo filter and action",
  "author": "Jane Doe",
  "version": "0.1.0",
  "wasmPath": "my-plugin.wasm",
  "filters": [
    {
      "methodName": "filterAsset",
      "title": "Only photos",
      "description": "Passes only image assets",
      "supportedContexts": ["Asset"],
      "schema": null
    }
  ],
  "actions": [
    {
      "methodName": "actionAddTag",
      "title": "Add tag",
      "description": "Adds a tag to the asset",
      "supportedContexts": ["Asset"],
      "schema": {
        "type": "object",
        "properties": {
          "tag": { "type": "string" }
        },
        "required": ["tag"]
      }
    }
  ]
}

```

The `methodName` fields must correspond to exported WASM functions. The `schema` field defines configuration UI using JSON Schema.

### Step 3: Deploy to the Plugin Directory

Place the plugin folder containing both [`manifest.json`](https://github.com/immich-app/immich/blob/main/manifest.json) and the `.wasm` file in either the core plugin path (`resourcePaths.corePlugin`) or the external install folder specified by `configRepository.getEnv().plugins.external.installFolder`. Immich scans these locations during `PluginService.onBootstrap`.

## Calling Immich APIs from Plugins

WASM plugins cannot directly access the database. Instead, they invoke **host functions** exposed by `PluginHostFunctions.getHostFunctions` in [`server/src/services/plugin-host.functions.ts`](https://github.com/immich-app/immich/blob/main/server/src/services/plugin-host.functions.ts). Available operations include `updateAsset` and `addAssetToAlbum`.

When a workflow trigger fires, `handleTrigger` queues a `WorkflowRun` job. The job runner calls your WASM function with a `PluginInput` object:

```ts
interface PluginInput<T = unknown> {
  authToken: string;          // JWT for permission validation
  config: T;                  // Workflow-specific configuration
  data: { asset: Asset };     // Triggering asset data
}

```

To update an asset from Rust:

```rust
use extism::CurrentPlugin;
use serde_json::json;

#[no_mangle]
pub extern "C" fn actionAddTag(cp: CurrentPlugin, offset: i64) -> i32 {
    let input: serde_json::Value = cp.read(offset).unwrap();
    let auth = input["authToken"].as_str().unwrap();
    let asset_id = input["data"]["asset"]["id"].as_str().unwrap();
    let tag = input["config"]["tag"].as_str().unwrap();
    
    let payload = json!({
        "authToken": auth,
        "id": asset_id,
        "title": format!("{} {}", input["data"]["asset"]["title"], tag)
    });
    
    cp.call_host("extism:host/user", "updateAsset", &payload.to_string());
    0
}

```

The host function validates the JWT token and enforces Immich's permission model before executing the update via `assetRepository.update`.

## Workflow Integration and Execution

After deployment, create a workflow in the Immich UI or API that references your plugin's filter and action IDs. When an event like `AssetCreate` fires, `handleAssetCreate` calls `handleTrigger`, which eventually invokes `handleWorkflowRun`. The system loads the workflow definition, applies filters to determine if the action should run, and executes the WASM function with the configured parameters.

Plugins receive a fresh authentication token for each invocation, ensuring secure, short-lived access credentials.

## Key Source Files for Plugin Development

- [`server/src/services/plugin.service.ts`](https://github.com/immich-app/immich/blob/main/server/src/services/plugin.service.ts): Core loader, manifest validation, and workflow integration
- [`server/src/services/plugin-host.functions.ts`](https://github.com/immich-app/immich/blob/main/server/src/services/plugin-host.functions.ts): Host function implementations (`updateAsset`, `addAssetToAlbum`)
- [`server/src/dtos/plugin-manifest.dto.ts`](https://github.com/immich-app/immich/blob/main/server/src/dtos/plugin-manifest.dto.ts): Validation schema for [`manifest.json`](https://github.com/immich-app/immich/blob/main/manifest.json)
- [`server/src/dtos/plugin.dto.ts`](https://github.com/immich-app/immich/blob/main/server/src/dtos/plugin.dto.ts): Data transfer objects for plugin metadata
- [`server/src/enums/enum.ts`](https://github.com/immich-app/immich/blob/main/server/src/enums/enum.ts): `PluginTriggerType` and `PluginContext` definitions

## Summary

- Write WASM modules in Rust, Go, or AssemblyScript using the Extism SDK
- Define capabilities in [`manifest.json`](https://github.com/immich-app/immich/blob/main/manifest.json) with filters, actions, and JSON schemas
- Deploy to core or external plugin directories scanned by `PluginService.onBootstrap`
- Interact with Immich data only through injected host functions that validate JWT tokens
- Configure workflows in the UI to trigger your plugin on events like `AssetCreate`

## Frequently Asked Questions

### What programming languages can I use to write Immich plugins?

You can use any language that compiles to WebAssembly and supports the Extism SDK. The immich-app/immich source code shows examples in Rust, but Go, AssemblyScript, C, and Zig are also compatible. The compiled output must be a `.wasm` file referenced by your [`manifest.json`](https://github.com/immich-app/immich/blob/main/manifest.json).

### How does Immich secure plugin execution?

Immich sandboxes plugins using the Extism runtime and validates all API calls through host functions in `PluginHostFunctions`. Each plugin invocation receives a unique JWT `authToken` that the host functions verify before performing operations like `updateAsset`. This ensures plugins operate within Immich's permission model and cannot access unauthorized data.

### Do I need to restart Immich to load new plugins?

The `PluginService` scans for plugins during bootstrap via `onBootstrap`. While a restart ensures `loadPluginsFromManifests` picks up new manifests, Immich also exposes a reload endpoint that triggers the loading sequence without a full restart. Place your plugin in the configured directory and either restart or hit the reload endpoint.

### Can plugins modify any asset metadata?

Plugins can only modify assets through the specific host functions exposed in `PluginHostFunctions`, such as `updateAsset` or `addAssetToAlbum`. Direct database access is prohibited. The host functions enforce field-level permissions and validate the plugin's JWT token before applying changes, ensuring data integrity and security.