Plugin Capabilities#
Audience. This is the developer-facing reference for the Lesstruct plugin capability manifest and host functions. It references source-tree paths (e.g.
internal/plugin/).If you are a Lesstruct user who has installed the binary and wants to add host function calls to a plugin, use the user-facing snapshot bundled with the
lesstruct-plugin-developmentskill atskills/lesstruct-plugin-development/references/plugin-capabilities.md. It covers the same contract (manifest schema, host functions, security model) but with no source-tree references.
Plugins can request access to host resources (HTTP, database, logging) by declaring them in a capability manifest file placed alongside the .wasm file.
Quick Start#
Create <plugin-name>.manifest next to <plugin-name>.wasm in the plugins/ directory:
| |
If no .manifest file exists, the plugin gets zero host functions — same as before.
Manifest Reference#
| Field | Required | Description |
|---|---|---|
name | Yes | Plugin name (must be non-empty) |
version | Yes | Semantic version of the plugin (must be non-empty) |
capabilities.http | No | List of allowed URL patterns |
capabilities.database | No | List of allowed database permissions |
URL Patterns#
URLs are matched using simple prefix matching. Use * as a suffix for wildcards:
| |
https://api.example.com/*matcheshttps://api.example.com/v1/data,https://api.example.com/foo/bar, etc.- Patterns are checked in order; the first match wins.
- The trailing
*is the only wildcard supported. Regex patterns are not accepted.
Database Permissions#
Database permissions follow the format <operation>:<table>:
| Permission | Allows |
|---|---|
read:content | SELECT queries on the content_items table |
read:media | SELECT queries on the media_files table |
read:users | SELECT queries on the users table |
write:content | INSERT/UPDATE/DELETE on the content_items table (including the custom_fields column) |
write:media | INSERT/UPDATE/DELETE on the media_files table |
write:users | INSERT/UPDATE/DELETE on the users table |
The host validates each SQL query to extract the target table name and checks it against the manifest.
Table Access Scope#
Of the 11 tables in the Lesstruct database, only three are grantable to
plugins: content_items (normalised to content), media_files
(normalised to media), and users. The other eight tables —
comments, blocked_emails, failed_login_attempts, verification_tokens,
password_reset_tokens, email_update_tokens, soft_deleted_content, and
api_keys — are inaccessible to plugins by design. A plugin cannot query or
modify them via db_query or db_exec.
Available Host Functions#
HTTP#
| Function | Signature | Description |
|---|---|---|
lesstruct.http_get | (url_ptr, url_len, headers_json_ptr, headers_json_len) -> result_offset | Perform an HTTP GET request |
lesstruct.http_post | (url_ptr, url_len, headers_json_ptr, headers_json_len, body_ptr, body_len) -> result_offset | Perform an HTTP POST request |
Database#
| Function | Signature | Description |
|---|---|---|
lesstruct.db_query | (sql_ptr, sql_len, params_json_ptr, params_json_len) -> result_offset | Execute a SELECT query |
lesstruct.db_exec | (sql_ptr, sql_len, params_json_ptr, params_json_len) -> result_offset | Execute an INSERT/UPDATE/DELETE |
Logging#
| Function | Signature | Description |
|---|---|---|
lesstruct.log_info | (message_ptr, message_len) -> void | Log an informational message to the host |
lesstruct.log_error | (message_ptr, message_len) -> void | Log an error message to the host |
Logging functions are always available when any manifest exists — no capability declaration needed.
Result Format#
All host functions return results as JSON at offset 4096 in WASM memory.
Success#
| |
Error#
| |
Offset collision. All four data host functions (
http_get,http_post,db_query,db_exec) write their result to the same fixed offset (4096). If your plugin callshttp_getand thendb_querybefore reading the first result, the second overwrites the first. Read each result before invoking the next host function.
Security Model#
| Concern | Mechanism |
|---|---|
| URL allowlisting | Each HTTP call is checked against the manifest’s http patterns before the request is made |
| Table-level DB access | SQL queries are parsed to extract the target table, then checked against database permissions |
| Response size limit | HTTP response bodies are capped at 1MB |
| Request timeout | HTTP requests have a 10-second timeout (set once at startup); DB queries use the parent request context |
| Memory isolation | Plugins remain in the WASM sandbox; host functions only write results to controlled offsets |
| Audit logging | Only denied host function calls are logged at the host level. Successful calls produce no log line. Use log_info from your plugin to record what you did. |
SQL Parser Limitations#
The host uses a hand-rolled substring parser to extract the target table
from each query. The parser looks for the first occurrence of FROM,
INSERT INTO, UPDATE, or DELETE FROM and reads the first identifier
after it.
The parser does not handle:
- Common Table Expressions (
WITH ... SELECT). - Subqueries.
- SQL comments (
--or/* */). - Quoted identifiers.
- Multi-statement queries.
Write a single explicit SELECT/INSERT/UPDATE/DELETE statement per
host function call.
Example: HTTP Enrichment Plugin#
A plugin that calls lesstruct.http_get to enrich a content item during
before_save:
| |
Manifest:
| |
Note. The Lesstruct SDK does not currently ship a
//go:wasmimportdeclaration forhttp_get(or any other host function). The declarations are bundled with thelesstruct-plugin-developmentskill atreferences/host-function-imports.go.txt— copy that block into yourmain.goto use the host functions.
Example: Database Plugin#
A plugin that reads content metadata during a before_save hook:
| |
Manifest:
| |