Skip to main content
Version: 0.1.x

Introduction​

Dynamic plugins are LinaPro's runtime-extensible plugin form. They compile plugins into .wasm artifacts that can be uploaded, installed, enabled, disabled, uninstalled, and explicitly upgraded at runtime — without recompiling the core framework.

Dynamic plugins run inside a WASM sandbox. They cannot directly access the core framework filesystem, network, or database; all core framework capability access must go through pluginbridge and hostServices authorization.

When to Use​

ScenarioDescription
Runtime hot-loadingUpload a .wasm artifact and it enters the plugin governance flow immediately
Temporary capability validationQuickly bring up a proof-of-concept feature; convert to a source plugin after validation
Commercial plugin distributionDistribute only binary artifacts without exposing source code
Controlled external integrationNetwork, storage, and data access are all governed through authorization snapshots

Long-term core business capabilities should still prefer source plugins. Dynamic plugins are better suited for hot-loading and scenarios with stronger isolation requirements.

What Is WebAssembly?​

WebAssembly (abbreviated WASM) is a stack-based virtual machine binary instruction format standardized by the W3C. Originally designed for the browser, it is now widely used in server-side, edge computing, and plugin system scenarios.

  • Cross-platform: WASM modules are platform-agnostic binaries. The same .wasm artifact runs on Linux, macOS, Windows, and across x86 and ARM instruction sets without recompilation.

  • Secure sandbox: WASM runs in a strictly isolated sandbox. By default, it cannot access the core framework filesystem, network, memory, or system calls. Every core framework capability must be explicitly authorized through an interface, fundamentally limiting the blast radius of malicious code or vulnerabilities.

  • Near-native performance: WASM uses a compact binary format that can be JIT-compiled to native machine code at runtime, achieving near-native execution efficiency while maintaining sandbox isolation.

  • Hot-loading support: WASM modules can be dynamically loaded and unloaded at runtime without restarting the core framework process. This provides a natural hot-update capability for plugin systems — new versions can go live or roll back without affecting overall system operation.

  • Multi-language ecosystem: Mainstream languages including Go, Rust, C/C++, and AssemblyScript can all compile to WASM. Plugin developers are not locked into a single tech stack. LinaPro currently uses Go as the primary plugin development language, extending the sandbox and core framework service communication contract based on WASI (WebAssembly System Interface).

Runtime Model​

The core framework completes authentication, authorization, and tenant context handling before passing a request snapshot into the WASM instance. Dynamic plugins see a structured request envelope — not a raw core framework internal object.

Directory Structure​

Dynamic plugin source directories follow the same layout as source plugins, with an additional main.go as the WASM entry point:

apps/lina-plugins/<plugin-id>/
├── main.go # WASM export function entry
├── plugin.yaml
├── plugin_embed.go
├── backend/
│ ├── api/ # API DTOs and route contracts
│ ├── internal/
│ │ ├── controller/ # HTTP controllers
│ │ ├── service/ # Business service layer
│ │ ├── dao/ # gf gen dao generated
│ │ └── model/ # do/entity models
│ └── plugin.go # Plugin registration entry
├── frontend/
│ └── pages/ # Plugin pages
├── manifest/
│ ├── sql/ # Installation and upgrade SQL
│ │ ├── mock-data/ # Demo data, optional
│ │ └── uninstall/ # Uninstall SQL
│ └── i18n/ # Plugin language packs
└── README.md

WASM Entry Point​

Dynamic plugins must export functions defined by the core framework contract. Here is an official example:

var guestRuntime = pluginbridge.NewGuestRuntime(dynamicbackend.HandleRequest)

//go:wasmexport lina_dynamic_route_alloc
func linaDynamicRouteAlloc(size uint32) uint32 {
return guestRuntime.Alloc(size)
}

//go:wasmexport lina_dynamic_route_execute
func linaDynamicRouteExecute(size uint32) uint64 {
responsePointer, responseLength, err := guestRuntime.Execute(size)
if err != nil {
fallback, _ := pluginbridge.EncodeResponseEnvelope(
pluginbridge.NewInternalErrorResponse(err.Error()),
)
responsePointer, responseLength, _ = guestRuntime.ExposeResponseBuffer(fallback)
}
return uint64(responsePointer)<<32 | uint64(responseLength)
}

//go:wasmexport lina_host_call_alloc
func linaHostCallAlloc(size uint32) uint32 {
return guestRuntime.HostCallAlloc(size)
}

func main() {}

Business routing is typically delegated to pluginbridge.MustNewGuestControllerRouteDispatcher, which dispatches requests to controller methods.

hostServices Authorization​

Dynamic plugins must declare the core framework services, methods, and resource scopes they need in plugin.yaml. When a plugin is installed or enabled, the core framework writes the authorization into a release snapshot. At runtime, any unauthorized call is rejected.

ServiceTypical Capabilities
runtimeLog writing, plugin status, time, UUID, node info
dataDatabase read/write constrained by table scope and tenant filtering
storageFile read/write within the plugin's namespace
networkExternal HTTP requests constrained by target address
cacheCluster-aware cache read/write
lockDistributed lock acquisition, renewal, and release
cronBuilt-in task registration for dynamic plugins
configPlugin configuration reading
notifyCore framework notification capability

Example:

hostServices:
- service: runtime
methods: [log.write, info.now, info.node]
- service: data
methods: [list, get, create, update, delete]
resources:
tables:
- plugin_demo_dynamic_record
- service: network
methods: [request]
resources:
- url: https://api.example.com

Building Dynamic Plugins​

Dynamic plugins use the standard Go toolchain compiled to the wasip1/wasm target. The project provides make commands to simplify the build process:

make wasm
make wasm p=plugin-demo-dynamic

Build artifacts are output to temp/output/<plugin-id>.wasm and include the plugin manifest, route contracts, and necessary embedded resources.

Installation, Enablement, and Upgrade​

The runtime flow for dynamic plugins:

  1. Build the .wasm artifact.
  2. Upload the dynamic plugin package in the admin workspace's Extension Center.
  3. The core framework validates the WASM file header, custom sections, embedded manifest, ABI version, and resources.
  4. The administrator confirms hostServices authorization.
  5. The installation SQL is executed and governance records are written.
  6. Once enabled, the core framework loads the WASM sandbox and projects routes, menus, and resources.

When a higher version is uploaded, the core framework does not immediately switch the active version. Instead, it marks the plugin as pending_upgrade. The administrator previews the diff in the plugin management page and explicitly executes the runtime upgrade. If the upgrade fails, the previous active version is retained and failure diagnostics are recorded for retry after fixing.

Key Differences from Source Plugins​

DimensionSource PluginWASM Dynamic Plugin
DeliverySource code compiled with the core framework.wasm runtime artifact
Hot-loadingRequires new core framework deploymentSupports runtime upload and enablement
PerformanceNative Go performanceSandbox and bridge overhead
Core framework capability accesspluginhost stable contracthostServices authorized bridge
Isolation strengthNamespace isolationWASM sandbox isolation
DebuggingStandard Go debug toolchainMore reliant on logs and bridge diagnostics
Best forLong-term business modulesCommercial distribution, hot-loading, temporary extensions

Best Practices​

  • Only request the hostServices methods and resource scopes you actually need.
  • Use the plugin ID namespace for database tables to avoid conflicts with the core framework or other plugins.
  • Be explicit about target addresses for outbound network access; avoid broad authorization.
  • Prepare rollback-friendly, idempotent upgrade SQL for runtime upgrades.
  • Promote long-running, high-frequency business logic to source plugins; use dynamic plugins for hot-loading and isolation scenarios.