mirror of
https://github.com/spacedriveapp/spacedrive.git
synced 2025-12-11 20:15:30 +01:00
- Implemented a complete WASM extension framework, enabling secure, sandboxed plugins. - Added core components including `PluginManager`, `host_functions`, and `permissions` for managing the lifecycle and security of extensions. - Integrated Wasmer runtime for executing WASM modules, enhancing the platform's extensibility. - Developed a demo extension showcasing the new API, significantly reducing boilerplate code and improving developer experience. - Updated documentation to reflect the new architecture and provide guidance for extension development. - Prepared for testing and validation of the extension system, marking a significant step towards a robust plugin ecosystem.
9.8 KiB
9.8 KiB
WASM Extension Architecture - Final Design
The Elegant Solution
ONE generic host function that reuses the entire existing Wire/Registry infrastructure.
┌─────────────────────────────────────────────────────────────────┐
│ Spacedrive Core │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ WASM Plugin Host (Wasmer Runtime) │ │
│ │ │ │
│ │ Finance.wasm Vault.wasm Photos.wasm ... │ │
│ │ │ │ │ │ │
│ │ └───────────────┴──────────────┘ │ │
│ │ │ │ │
│ │ │ All call: │ │
│ │ ▼ │ │
│ │ spacedrive_call(method, lib_id, payload) │ │
│ │ │ │ │
│ └────────────────────────┼───────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ RpcServer::execute_json_operation() │ │
│ │ (EXISTING - used by daemon RPC!) │ │
│ └────────────────────────┬─────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Operation Registry (inventory crate) │ │
│ │ │ │
│ │ LIBRARY_QUERIES: │ │
│ │ • "query:ai.ocr.v1" → OcrQuery::execute() │ │
│ │ • "query:ai.classify_text.v1" → ClassifyQuery::exec() │ │
│ │ • ... │ │
│ │ │ │
│ │ LIBRARY_ACTIONS: │ │
│ │ • "action:vdfs.create_entry.input.v1" → Create::exec()│ │
│ │ • "action:vdfs.write_sidecar.input.v1" → Write::exec()│ │
│ │ • ... │ │
│ └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
The Complete API
Host Functions (Rust → WASM)
Total: 2 functions
#[link(wasm_import_module = "spacedrive")]
extern "C" {
/// Generic operation call - routes to Wire registry
fn spacedrive_call(
method_ptr: *const u8, // Wire method string
method_len: usize,
library_id_ptr: u32, // 0 = None, else UUID bytes
payload_ptr: *const u8, // JSON input
payload_len: usize
) -> u32; // Returns JSON output ptr
/// Logging helper
fn spacedrive_log(level: u32, msg_ptr: *const u8, msg_len: usize);
}
Extension SDK (Wrapper)
// spacedrive-sdk provides ergonomic API
pub struct SpacedriveClient {
library_id: Uuid,
}
impl SpacedriveClient {
// Type-safe operations
pub fn create_entry(&self, input: CreateEntryInput) -> Result<Uuid>;
pub fn write_sidecar(&self, entry_id: Uuid, filename: &str, data: &[u8]) -> Result<()>;
pub fn ocr(&self, data: &[u8], options: OcrOptions) -> Result<OcrOutput>;
pub fn classify_text(&self, text: &str, prompt: &str) -> Result<serde_json::Value>;
// Generic caller for any Wire operation
pub fn call<I, O>(&self, method: &str, input: &I) -> Result<O>
where I: Serialize, O: DeserializeOwned;
}
Extension Code Example
use spacedrive_sdk::SpacedriveClient;
fn process_receipt(email: Vec<u8>, client: &SpacedriveClient) -> Result<Uuid> {
// Clean, type-safe API
let entry_id = client.create_entry(CreateEntryInput {
name: "Receipt: Starbucks",
path: "receipts/new.eml",
entry_type: "FinancialDocument",
})?;
client.write_sidecar(entry_id, "email.json", &email)?;
let pdf = extract_pdf(&email)?;
let ocr = client.ocr(&pdf, OcrOptions::default())?;
let receipt = client.classify_text(&ocr.text, "Extract receipt data")?;
client.write_sidecar(entry_id, "receipt.json", &serde_json::to_vec(&receipt)?)?;
Ok(entry_id)
}
Implementation Checklist
Core Components (~700 lines total)
1. WASM Plugin Manager (core/src/infra/extension/manager.rs)
- Load WASM modules with Wasmer
- Plugin lifecycle (init/cleanup)
- Hot-reload support
- Plugin registry
- ~300 lines
2. Host Functions (core/src/infra/extension/host_functions.rs)
host_spacedrive_call()- Generic Wire RPChost_spacedrive_log()- Logging helper- Memory helpers (read/write WASM memory)
- Bridge to
execute_json_operation() - ~100 lines
3. Permission System (core/src/infra/extension/permissions.rs)
- Load permissions from manifest
- Check method permissions
- Rate limiting
- Resource limits (via Wasmer)
- ~200 lines
4. Extension SDK (spacedrive-sdk/src/lib.rs)
SpacedriveClientwrapper- Type-safe operation methods
- WASM memory management
- Error handling
- ~400 lines (separate crate)
New Operations to Register (~500 lines)
AI Operations:
OcrQuery- Extract text from images/PDFsClassifyTextQuery- AI text classificationGenerateEmbeddingQuery- Semantic embeddings
Credential Operations:
StoreCredentialAction- Save OAuth tokensGetCredentialQuery- Retrieve credentials (auto-refresh)
VDFS Operations:
WriteSidecarAction- Store sidecar filesReadSidecarQuery- Read sidecar filesUpdateMetadataAction- Update entry metadata
HTTP Operations (for WASM):
HttpRequestQuery- Proxy HTTP calls for extensions
Timeline
Week 1-2: WASM Runtime
- Integrate Wasmer
- Load basic .wasm module
- Call
plugin_init()
Week 3: Wire Bridge
- Implement
host_spacedrive_call() - Connect to
execute_json_operation() - Test calling existing operations
Week 4-5: Operations
- Add AI operations
- Add credential operations
- Add VDFS sidecar operations
- Add HTTP proxy
Week 6: SDK
- Build
spacedrive-sdkcrate - Type-safe wrappers
- Documentation
- Publish to crates.io
Week 7+: Finance Extension
- Build receipt processing logic
- Compile to WASM
- Test end-to-end
- Launch!
The Key Decisions
1. WASM-Only (No Process-Based)
Rationale:
- WASM gives us: security, distribution, hot-reload, universality
- Implementation complexity is low (~700 lines)
- Timeline is reasonable (6-7 weeks)
- Gets us the platform benefits immediately
2. Generic spacedrive_call() (Not Per-Function FFI)
Rationale:
- Minimal API surface (2 functions vs. 15+)
- Perfect code reuse (operation registry)
- Zero maintenance (add operations without touching host)
- Type safety via Wire trait
3. HTTP Proxy Host Function
Rationale:
- WASM can't make HTTP calls directly
- Extensions need OAuth/API access
- Controlled via manifest permissions
- More secure than native network access
Next Steps
- Review This Design with team
- Prototype
host_spacedrive_call()bridging toexecute_json_operation() - Add First Operation (e.g.,
ai.ocr) - Test From WASM module
- Build Finance Extension in parallel with platform
Questions to Resolve
- HTTP Proxy: How restrictive? Allow any domain in manifest, or curated list?
- Async in WASM: Use
wasm-bindgen-futuresor make host functions blocking? - Error Handling: Return errors as JSON
{error: "..."}or throw WASM traps? - Event Subscriptions: How do WASM extensions subscribe to events?
- Job Execution: Should WASM extensions define jobs, or just trigger core jobs?
Recommendations:
- Allow any domain if in manifest
allowed_domains - Make host functions blocking (simpler), use Tokio runtime internally
- Return errors as JSON (more graceful)
- Extensions export callback functions, host calls them when events fire
- Extensions trigger core jobs via
jobs.dispatch(don't define custom job types yet)
Ready to start implementation: begin with WASM runtime integration and host_spacedrive_call()!