Authoring native (Rust) plugins¶
The sandboxed loaders (WASM Component Model, Extism, Rhai, PyO3) cover scalar functions, aggregates, and procedures. Native Rust plugins are the only kind that can author every extension surface — operators, index kinds, storage backends, CRDTs, hooks, triggers, graph algorithms, logical types, connectors, and more — because those surfaces are in-process Rust traits with no cross-ABI wire format.
A native plugin is a Rust type that implements the Plugin trait and is added
to the database with Uni::add_plugin. It is trusted: it runs in-process with no sandbox, so
only load native plugins you compiled or audited.
The Plugin trait¶
The trait is deliberately small — all per-surface detail lives in the capability traits. A plugin is a bundle of registrations:
use uni_plugin::{Plugin, PluginManifest, PluginRegistrar, PluginError};
pub struct GeoPlugin {
manifest: PluginManifest,
}
impl Plugin for GeoPlugin {
fn manifest(&self) -> &PluginManifest {
&self.manifest
}
fn register(&self, r: &mut PluginRegistrar<'_>) -> Result<(), PluginError> {
r.scalar_fn(/* qname, signature, impl */)?;
Ok(())
}
// `init(&self, cx)` and `shutdown(&self)` are optional (default no-ops).
}
manifest()returns the plugin'sPluginManifest— id (reverse-DNS), version, ABI range, declared capabilities, determinism, and an optional Ed25519 signature.register()is called once at load time with aPluginRegistrar. Every registration is capability-gated and staged; the registrar commits atomically only ifregister()returnsOk.init()/shutdown()are optional lifecycle callbacks.
Add it to a database:
let db = Uni::in_memory().build().await?;
db.add_plugin(GeoPlugin::new())?; // trust policy (signature) is enforced here
Uni::add_plugin<P: Plugin>(&self, plugin: P) -> Result<()> validates the manifest against the
host trust policy, runs register(), and commits.
Surfaces, traits, and registrar methods¶
Each surface is a trait in uni_plugin::traits plus a PluginRegistrar method that registers an
implementation. The method requires the matching capability; a
plugin whose manifest didn't declare it fails registration with PluginError::CapabilityRequired.
| Surface | Trait | Registrar method |
|---|---|---|
| Scalar function | ScalarPluginFn |
scalar_fn(qname, sig, f) |
| Aggregate function | AggregatePluginFn |
aggregate_fn(qname, sig, f) |
| Window function | WindowPluginFn |
window_fn(qname, sig, f) |
| Procedure | ProcedurePlugin |
procedure(qname, sig, p) |
| Physical operator | OperatorProvider |
operator(qname, p) |
| Optimizer rule | OptimizerRuleProvider |
optimizer_rule(r) |
| Index kind | IndexKindProvider |
index_kind(kind, p) |
| Storage backend | StorageBackend |
storage_backend(scheme, b) |
| CRDT kind | CrdtKindProvider |
crdt_kind(kind, p) |
| Session / query hook | SessionHook |
hook(h) |
| Trigger | TriggerPlugin |
trigger(t) |
| Graph algorithm | AlgorithmProvider |
algorithm(qname, p) |
| Pregel program | PregelProgramProvider |
pregel(qname, p) |
| Locy aggregate | LocyAggregate |
locy_aggregate(qname, a) |
| Locy predicate | LocyPredicate |
locy_predicate(qname, sig, p) |
| Logical (Arrow extension) type | LogicalTypeProvider |
logical_type(t) |
| Authentication | AuthProvider |
auth_provider(p) |
| Authorization | AuthzPolicy |
authz_policy(p) |
| Connector / wire protocol | Connector |
connector(c) |
| Collation | CollationProvider |
collation(c) |
| CDC output sink | CdcOutputProvider |
cdc_output(c) |
| Catalog / virtual schema | CatalogProvider |
catalog(c) |
| Background job | BackgroundJobProvider |
background_job(j) |
Registrar methods return Result<&mut Self, PluginError>, so registrations chain:
// A plugin must register within its own namespace — the QName namespace has to
// match the manifest id (here `ai.example.geo`), or registration is rejected.
fn register(&self, r: &mut PluginRegistrar<'_>) -> Result<(), PluginError> {
r.scalar_fn(QName::new("ai.example.geo", "haversine"), sig_haversine(), Arc::new(Haversine))?
.scalar_fn(QName::new("ai.example.geo", "bearing"), sig_bearing(), Arc::new(Bearing))?;
Ok(())
}
A scalar implementation is an Arc<dyn ScalarPluginFn> that exposes its FnSignature and an
invoke method over Arrow batches. See the built-in identity scalar in
crates/uni-plugin-builtin/src/scalar_fns/mod.rs for the full shape, including FnSignature
(ArgType / NullHandling) and error reporting via FnError.
Window functions
WindowPluginFn is implementable today, but no built-in window function ships as a reference
yet — model it on the scalar/aggregate built-ins.
Built-in plugins are the reference¶
Every built-in in uni-db is itself a native plugin, so the source is the canonical worked example for each non-trivial surface:
- Index kind —
crates/uni-plugin-builtin/src/index_vector.rs(the exact-KNN vector index;IndexKindProvider+ the build / handle traits). - CRDTs —
crates/uni-plugin-builtin/src/crdts.rs(lww-register,or-set,g-counter,mv-register,rga;CrdtKindProvider+CrdtState). - Storage backend —
crates/uni-plugin-builtin/src/storage.rs(the Lance backend;StorageBackend+Storage). - Scalars —
crates/uni-plugin-builtin/src/scalar_fns/mod.rs.
Capabilities and trust¶
A native plugin still declares capabilities in its manifest. add_plugin intersects them with the
host grant set the same way the sandboxed loaders do, and the registrar enforces the result — so a
plugin that didn't declare Crdt cannot call crdt_kind, even in-process. Host-import capabilities
(Network, Filesystem, HostQuery, Kms, Secret, Config) gate the host services a plugin
may call. See Trust & Capabilities for the signature policy and the
declared-∩-granted model, and Reference for the full capability list.
See also¶
- Concepts — the plugin / manifest / registrar / registry model.
- Authoring (sandboxed loaders) — WASM Component Model and Extism.
- Trust & Capabilities — signatures, trust roots, capability intersection.
- Reference — load APIs, capability names, and resource quotas.