You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Being able to extend an application at runtime is something that only few systems can do. Operating Systems and Browsers are some of the few, but there are also plenty of apps that allow plugins (Raycast, VSCode, Wordpress). This is part of what makes these systems incredibly powerful and versatile. I'd love an Atomic Server to be similar to these, in some regards: it should be able to run apps, which should be able to do pretty much whatever a developer might want.
Atomic Server provides a really cool extra bonus: It runs on the web! You can access your atomic server and all its data and apps from any device.
In order to make this plugin abstraction well designed, we should extensively use it internally, during development of (core) features.
Naming: apps or plugins?
Apps sound more powerful, Plugins are more technical
Apps feel like they take over the entire screen
Plugins add functionality to something that already exists
I'll mostly use plugin from now on.
Plugin or Core?
One of the hardest questions when I'm trying to extend Atomic-Server, is whether it's a Plugin, or whether it's Core functionality.
Core is available on any atomic server. This means that a Plugin can always use Core features.
Some usecases
Let's describe some apps and try to identify which kind of abstractions that would be required to realize this.
Host endpoint for queries (fetch version x of resource y, show all commits for resource y, etc.)
Extend existing resources (not sure if this is necessary, but it might come in handy for adding a versions property to existing resources, which increases discoverability)
Custom views (e.g. a version browser, like hackMD or Google Docs has)
Might be part of the core API instead of plugin, but trying to design this as a plugin basically helps us to design a powerful abstraction layer.
Command for 'freezing' resources on request. Needs authorization.
Resolve urls that start with ifps
Run IPFS conversion logic
Store to disk (K/V api, or maybe provide access to disk? rust-ipfs uses sled, so that might make things easier in this case)
Network access / connect to swarm
Sys monitor
HTML page that refreshes every second
Shows hardware system stats like memory usage, CPU, disk size
Possibly shows running plugins, option to kill processes.
SPARQL endpoint
Use something like Oxigraph as a library. It features both a MemoryStore as well as two persistent options, one using Sled (which this project uses!) and one using RocksDB
User should be able to have some form of authorization
Maybe serve an HTML + JS browser app
Create commits from SPARQL UPDATE queries. This means that the SPARQL endpoint must be able to sign commits. Maybe because it has a Private Key, or because it has some token that is publicly verifiable and only usable in this scope?
Simple CRUD models endpoints
an HTTP endpoint for CRUD operations for one specific class, which feels like a very common JSON endpoint.
POST plain JSON objects (with shortnames as keys) to some endpoint, the plugin creates Commits.
Perhaps some authorization token to include?
TODO MVC App
A simple HTML + JS webapp with some client side logic that uses an Atomic Server, possibly the CRUD example from above?
Runtime options
WASM
I'm very enthousiastic about WASM runtimes, such as Wasmer and WasmEdge. WASM is a compilation target for many languages, which means that many developers could write for it. It can be very performant, just a little short of full native. WASM executables can be shared as byte arrays. A wasm runtime can be sandboxed, and might be given access to certain system abscrations (e.g. filesystem / networking). The WASI spec aims to standardize these types of system interfaces.
Some projects for inspiration, that have implemented a WASI / WASM plugin system:
Feather which has a wasmer plugin system called quill. It stores system data using wasmer::ctx.
Interesting project with nice docs, but it doesn't seem to support calling host functions from within a plugin. Update: just read in discord that calling host functions is now possible in one branch. Uses unsafe, though.
JS
JS is very popular and a ton of modules can be used.
I've explored QuickJS-rs, but kind of got stuck because I don't think it's possible to limit the runtime (e.g. no network access). There is no fetch.
WasmEdge also has JS execution by porting QuickJS to WASM, but does provide ways to limit access of the runtime to networking, for example. It also does have fetch support.
Adding a plugin should be as simple as possible. Ideally, it can be done using a web API without rebooting the server.
Owner posts a commit to the server, describing a Installation. This commit contains a (ipfs link to the) WASM byte array containing the logic. It optionally contains a link to a Config instance, which should be applied in another Commit.
Server checks commit, applies it
Server checks code for correct version. If
Server checks code for registration hooks, so the server knowns when the plugin needs to be called. (e.g. periodic register, specific endpoint, specific class). These registrations are probably saved in memory, but they also need to be constructed when the server boots. Maybe store registrations on disk first?
WASM code is loaded into memory, so it can be called when necessary
Functions are called when needed
Configuring a plugin
An Installation resource has an optional configuration field, which links to a Configuration Resource.
These can be edited like any other resource.
When a configuration is updated, the Plugin should re-initialize / re-load. Not sure how this should work, but I'll probably need some sort of callback handler thing that listens to changes to specific resources.
Removing a plugin
Another Commit is made, which removes the plugin
The registrations are removed.
The WASM code is loaded out of memory.
Updating a plugin
Another Commit is made which
When the code can be called
Periodically: similar to cron jobs. Maybe the plugin should be able to determine which handlers (e.g. every 10 minuten). Perhaps reserve one thread for this - or find a more elegant solution with actix actors.
On specific endpoints: e.g. /ipfs
Before returning instances of Classes: e.g. all Invites, which uses query params similar to Endpoints
Before applying a Commit: e.g. before saving an Invite, you need to check the Rights of the one making the Commit.
When certain resource conditions are met: (e.g. if a certain class or property is present)
By other plugins: See below.
Inter-plugin dependencies
Plugins might call methods from other plugins, and therefore have dependencies to other plugins. For example, the Calendar plugin might use the versions plugin to undo changes to calendar items.
Not sure how this will work, but it seems like a really powerful thing.
Front-end
Users are very likely to extend the views, perhaps even more than the back-end. Should a Plugin host it's own JS + HTML? Should it link to some URL of a component? Does it depend on Atomic-Data-Browser, or not per say?
@Polleps has just created a first draft for front-end plugins. However, this opens up some serious questions about safety. We can dynamically import javascript from plugins, but this gives that JS access to a lot of sensitive stuff (e.g. the user's key, or sensitive data loaded in @tomic/lib). I don't think this should be technically possible.
One solution is to have a centralized, controlled repository where we (or some other trusted group) vets the changes made to plugins to prevent malicious or dangerous code. But this is
I hope there are other ways (iframes?) to solve this.
Plugin discovery
Ideally, users open a plugin store (store, marketplace, whatever) where they see the plugins, click one, and it installs.
We could do this by:
Managing it in atomic. We have a "plugin list" resource that we manage, which links to a bunch of plugins (across the globe).
Manage it in the repo. More secure, more controlled, but also harder as a dev to publish i suppose.
Being able to extend an application at runtime is something that only few systems can do. Operating Systems and Browsers are some of the few, but there are also plenty of apps that allow plugins (Raycast, VSCode, Wordpress). This is part of what makes these systems incredibly powerful and versatile. I'd love an Atomic Server to be similar to these, in some regards: it should be able to run apps, which should be able to do pretty much whatever a developer might want.
Atomic Server provides a really cool extra bonus: It runs on the web! You can access your atomic server and all its data and apps from any device.
In order to make this plugin abstraction well designed, we should extensively use it internally, during development of (core) features.
Naming: apps or plugins?
I'll mostly use plugin from now on.
Plugin or Core?
One of the hardest questions when I'm trying to extend Atomic-Server, is whether it's a Plugin, or whether it's Core functionality.
Some usecases
Let's describe some apps and try to identify which kind of abstractions that would be required to realize this.
Versioning / history #42
versionsproperty to existing resources, which increases discoverability)Auditing #95
Calendar app
Full text search #40
IPFS storage #66
Sys monitor
SPARQL endpoint
MemoryStoreas well as two persistent options, one using Sled (which this project uses!) and one using RocksDBSimple CRUD models endpoints
TODO MVC App
Runtime options
WASM
I'm very enthousiastic about WASM runtimes, such as Wasmer and WasmEdge. WASM is a compilation target for many languages, which means that many developers could write for it. It can be very performant, just a little short of full native. WASM executables can be shared as byte arrays. A wasm runtime can be sandboxed, and might be given access to certain system abscrations (e.g. filesystem / networking). The WASI spec aims to standardize these types of system interfaces.
Some projects for inspiration, that have implemented a WASI / WASM plugin system:
wasmer::ctx.fp-bindgenWASM + fp-bindgen
atomic_lib::Resourcetype, as it supportsHashMapand most low-level types. Cool stuff!.witbased approach is the future, but fp-bindgen is nice for the short term.wasmtime + wit
.witfiles are type definitions for modules that can be used in thewasmtimeruntime to call typed WASM functions using a host-plugin system..witfiles from rust, although it's currently outdated.WASMER + WAI
https://wasmer.io/posts/wasmer-takes-webassembly-libraries-manistream-with-wai
Fork of wit-bindgen. See issue.
EXTISM
https://extism.org/
Interesting project with nice docs, but it doesn't seem to support calling host functions from within a plugin. Update: just read in discord that calling host functions is now possible in one branch. Uses unsafe, though.
JS
JS is very popular and a ton of modules can be used.
I've explored QuickJS-rs, but kind of got stuck because I don't think it's possible to limit the runtime (e.g. no network access). There is no fetch.
WasmEdge also has JS execution by porting QuickJS to WASM, but does provide ways to limit access of the runtime to networking, for example. It also does have fetch support.
I've also experimented with GreenCopperRuntime, which is really amazing, but also a bit too young to use.
Plugin API
Installation / registration
Adding a plugin should be as simple as possible. Ideally, it can be done using a web API without rebooting the server.
Installation. This commit contains a (ipfs link to the) WASM byte array containing the logic. It optionally contains a link to a Config instance, which should be applied in another Commit.Configuring a plugin
Installationresource has an optionalconfigurationfield, which links to aConfigurationResource.Removing a plugin
Updating a plugin
When the code can be called
/ipfsInvites, which uses query params similar to EndpointsInvite, you need to check the Rights of the one making the Commit.Inter-plugin dependencies
versionsplugin to undo changes to calendar items.Front-end
Users are very likely to extend the views, perhaps even more than the back-end. Should a Plugin host it's own JS + HTML? Should it link to some URL of a component? Does it depend on Atomic-Data-Browser, or not per say?
@Polleps has just created a first draft for front-end plugins. However, this opens up some serious questions about safety. We can dynamically import javascript from plugins, but this gives that JS access to a lot of sensitive stuff (e.g. the user's key, or sensitive data loaded in @tomic/lib). I don't think this should be technically possible.
One solution is to have a centralized, controlled repository where we (or some other trusted group) vets the changes made to plugins to prevent malicious or dangerous code. But this is
I hope there are other ways (iframes?) to solve this.
Plugin discovery
Ideally, users open a plugin store (store, marketplace, whatever) where they see the plugins, click one, and it installs.
We could do this by: