Memona plugins
Plugins connect Memona to additional filesystems through a shared API. The app and its folder agents use the same connection service and permission checks.
- Using Memona: start with Using plugins.
- Building a plugin: follow Build a plugin.
- Looking up a function: open the generated API reference.
- Publishing: visit the plugin catalog and verify your email before uploading a release.
How it works
A plugin is a portable WebAssembly component. Memona’s Rust backend hosts it using Wasmtime and calls the filesystem functions described by WIT. WIT defines the contract; WASI provides selected system interfaces. The backend supplies network access while retaining host-enforced transport and resource limits.
Plugin authors can use the Rust SDK without copying Memona’s WIT files into their own repository. There is no Python or JavaScript backend interpreter. The same installed backend serves both the native app window and browser clients connected to it; plugins do not depend on a browser tab remaining open.
Initial scope
The initial API covers filesystem providers, connection configuration and agent file tools. WebDAV and IPFS integrations are planned provider implementations; this release does not bundle them. The repository includes a test component that demonstrates the contract.
Custom viewers and editors using Web Components are a future extension point. Version 1 packages do not execute frontend JavaScript or install scripts. Remote offline synchronization is also outside this initial API delivery.
Memona keeps its existing folders-as-pages model. Plugins do not introduce a mandatory vault structure.
Using plugins
Open Plugins from Memona’s menu. The dialog has three sections:
- Browse searches the public catalog and shows package details.
- Installed manages installed versions and enabled state.
- Connections configures each provider and its agent permissions.
Install and update
Select a catalog entry to inspect the publisher, version and description. Choose Install to download that exact release. Memona validates the downloaded package and its digest before activation. You do not need a publisher account to browse or install.
Updates are explicit. A failed update leaves the previous installed version in place. Installed plugins can contact public and private network services. Agent access remains controlled by each connection’s settings.
Disabling a plugin stops its connections and prevents new operations. Removing a plugin does not delete files stored on a remote service. If the catalog is unavailable, installed plugins can still be managed; remote operations still need access to their configured remote service.
Connections and agents
A provider can have several connections with separate configuration. Set agent access independently for each connection:
| Setting | What Memona’s agent file tool can do |
|---|---|
| Off | The connection is not available to agents. |
| Read only | Inspect folders and read files. |
| Read and write | Read and request supported changes. |
The provider may itself be read-only. Granting write access cannot add an operation that the provider does not support. Agent grants apply to Memona’s remote-file tool; they do not change what an external agent’s independent shell or local filesystem tools can access.
Enter the server address and credentials in the provider’s connection fields. There is no second network allowlist to configure. Plugins may contact public, LAN, VPN, link-local and loopback addresses. A plugin declares its protocol’s socket ports, and Memona applies them automatically.
Credentials belong to a connection. Secret fields are kept in backend memory and are not shown back to the frontend or written to the connection file. After restarting the backend, enter required secrets again before reconnecting.
Publishing
Use the publisher link to open plugin.memona.io. Publishing and email verification happen there. Installation happens inside Memona; there is no custom install-link protocol.
The website also provides plugin guides, publisher profiles and sorting by updates, downloads or likes. You can sign in to like a plugin without becoming a publisher. Downloads count package requests rather than installed copies or individual people. Daily activity uses UTC and distinguishes dates before tracking began from dates with zero downloads.
Agent access
Installed filesystem providers share one Memona agent tool, remote_files.
Enable access for each connection in Plugins → Connections. New connections
default to Off. Read only permits browsing and reading; Read and write also
permits supported changes.
An agent first discovers connections:
{"operation": "connections"}
Only connections with an enabled plugin and an agent grant appear. Use the returned connection ID for subsequent calls:
{
"operation": "list",
"connectionId": "the-returned-connection-id",
"path": "/",
"cursor": null,
"limit": 100
}
To read part of a file:
{
"operation": "read",
"connectionId": "the-returned-connection-id",
"path": "/notes.md",
"offset": 0,
"length": 65536
}
File bytes are JSON arrays of integers from 0 through 255. A read includes an opaque revision when the provider supports it and an end-of-file flag. Continue with the next offset to read a larger file. Each call transfers at most 1 MiB.
Changing a file
A write replaces the entire file. Always choose a condition:
| Condition | Behavior |
|---|---|
{"kind":"createOnly"} | Fail if a file already exists. |
{"kind":"ifMatch","revision":"opaque-token"} | Replace only the revision you read. |
{"kind":"overwrite"} | Explicitly replace the current contents. |
Conditional replacement requires provider support. An unsupported condition fails; the host never silently changes it to overwrite. A file larger than the write limit cannot be saved with this API version. Splitting it into multiple writes would repeatedly replace the file, not append chunks.
If a mutation reports outcomeUnknown, inspect the remote state before deciding
whether to retry: the service may have completed it before the response was lost.
These resources are connection-relative paths, not operating-system mounts. An agent’s ordinary shell and local file tools do not automatically access them. Changing the grant or disabling the plugin affects subsequent calls and cancels active work for that connection.
Build a plugin
The initial SDK targets Rust and WASI 0.2 components. Install the component target:
rustup target add wasm32-wasip2
The reference implementation uses Rust 1.98. Plugin source can use compatible Rust releases, but its generated component must satisfy the supported WIT API. Native subprocess, OS-specific and unrestricted networking crates are not automatically available inside a component.
Start with the SDK
The SDK lives at plugin_system/sdk in the
Memona repository. Until a crates.io
release is published, use a checked-out, pinned Memona revision and a path
dependency:
[package]
name = "my-memona-plugin"
version = "0.1.0"
edition = "2024"
[lib]
crate-type = ["cdylib"]
[dependencies]
memona-plugin-sdk = { path = "../memona/plugin_system/sdk" }
The path is relative to your own Cargo.toml; adjust it to your checkout. Pin
the checkout revision so your build does not silently pick up an API change.
The SDK includes the canonical WIT and generated Rust types. Implement its
Guest trait and invoke its export! macro for your implementation type. You
do not need wasm-bindgen, wasm-pack, or WIT copies in your plugin repository.
cargo doc also exposes the generated Rust API.
struct MyProvider;
// Implement every method of memona_plugin_sdk::Guest for MyProvider.
// Unsupported optional operations return ErrorCode::Unsupported.
memona_plugin_sdk::export!(MyProvider);
This short excerpt shows registration, not a complete implementation. The full
buildable example is plugin_system/test-component/src/lib.rs. It is a test
fixture, not a WebDAV or IPFS client. From a Memona checkout, build it with:
cargo build --manifest-path plugin_system/Cargo.toml \
-p memona-plugin-test-component --target wasm32-wasip2 --release
Build your own plugin with:
cargo build --release --target wasm32-wasip2
Call the host
Use the SDK’s generated host interface for network requests. For example, this helper retrieves a bounded HTTP response through the connection’s permissions:
use memona_plugin_sdk::memona::filesystem::host;
pub fn get_bytes(url: &str) -> Result<Vec<u8>, host::HostError> {
let response = host::http(&host::Request {
method: "GET".into(),
url: url.into(),
headers: Vec::new(),
body: Vec::new(),
})?;
if !(200..300).contains(&response.status) {
return Err(host::HostError::Unavailable);
}
Ok(response.body)
}
Map HTTP statuses to appropriate filesystem errors in a real provider. Supply authentication through request headers using connection configuration; do not hard-code tokens. The host resolves and pins addresses on every request and on every redirect it follows.
Nothing crosses whole. A request body is pushed in pieces of at most 1 MiB and
a response body is pulled the same way, so a file of any size moves without
either side holding it. The same shape governs reader and writer, which is
how a provider serves a file larger than one call allows.
A protocol that is not HTTP uses connect for a TCP stream or bind for a
datagram socket, each on a package-declared port. The host resolves and pins
DNS and can start TLS so certificate checking stays outside the guest.
Prepare a release
Copy the resulting .wasm component to plugin.wasm, write
manifest.json, and ZIP the files at the archive root. Name the
archive with a .memona-plugin suffix. A directory wrapping the files inside
the ZIP is not accepted.
Use the filesystem behavior guide to handle revisions, unsupported operations and mutation failures correctly. Before publishing, exercise your provider against its real service and test lost connections.
Package format
A .memona-plugin package is a ZIP archive with these root entries:
manifest.json required
plugin.wasm required WebAssembly component
README.md optional UTF-8 text
LICENSE optional UTF-8 text
icon.png optional PNG
Version 1 rejects other entries, duplicate paths, directories, symlinks, encrypted archives and paths that could escape the archive root. There are no install hooks or executable frontend assets.
Guide and icon
Include a root-level README.md to provide the guide on the plugin’s public
page. Write UTF-8 Markdown and explain setup, required services, connection
fields, supported operations and limitations. Keep credentials and other private
information out of the guide. A release without a README shows a missing-guide
message; the store does not invent documentation from its manifest.
The website renders headings, lists, links, code and tables as selectable text.
Raw HTML is disabled and rendered content is sanitized. Links may use HTTP,
HTTPS or mailto; remote images become links or descriptive text instead of
loading a third-party image. Use fenced code blocks for examples and ordinary
links for external diagrams or screenshots.
An optional root-level icon.png supplies the icon on catalog cards and the
plugin page. It must be a PNG no larger than 1 MiB. A missing icon uses the
plugin’s initial. The store serves the validated package asset from its own
origin; a manifest cannot point the icon at another server.
README and icon belong to an immutable release. Publish a new version to change them. Both are extracted through the isolated package validator, including when the store first reads metadata from an older release. Assets and guides from withdrawn releases are unavailable through public release routes.
Manifest
{
"schemaVersion": 2,
"id": "your-publisher/my-provider",
"name": "My filesystem",
"version": "0.1.0",
"apiVersion": "0.1.0",
"description": "Connect to my filesystem service.",
"license": "MIT",
"entry": "plugin.wasm",
"providers": [
{
"id": "files",
"name": "My filesystem",
"schemes": ["myfiles"],
"configFields": [
{"key": "endpoint", "label": "Service URL", "kind": "text", "required": true},
{"key": "token", "label": "Access token", "kind": "password", "required": true}
]
}
]
}
Replace the example publisher with your own. Your verified publisher account must own the publisher portion of the package ID.
| Field | Meaning |
|---|---|
schemaVersion | Manifest shape; new packages use 2. |
id | Immutable publisher/plugin identifier. |
version | Canonical semantic release version. |
apiVersion | Memona plugin API version; currently 0.1.0. |
entry | Must be plugin.wasm in this format. |
networkPorts | Declared port spans for sockets, as { "from": 21, "to": 21 }. |
providers | Stable provider identities and declarative configuration fields. |
Publisher/plugin/provider slugs use lowercase ASCII letters, digits and hyphens,
begin and end with a letter or digit, and have at most 64 characters. Human names
can use other languages. Configuration kind is text, password or boolean.
Never place a credential in a manifest or a password default. Publishing
rejects a member the catalog does not know, so a typo never ships, while an
app older than a member ignores it, so a newer catalog never breaks an
installed app.
Plugins can contact any public, LAN, VPN, link-local or loopback host through the host’s HTTP and socket services. There is no host allowlist or private network grant in the manifest or connection configuration. ENS or other name-resolution protocols can contact their RPC nodes and content gateways; the plugin implements resolution before making ordinary network requests.
Current Memona versions also read schema 1 packages, ignoring their obsolete
host declarations. New publications use schema 2. The legacy catalog API keeps
the old manifest shape for older apps; new clients use /api/v2.
An empty networkPorts list declares no sockets, which is every provider that
speaks only HTTP. A protocol that negotiates its data channel at runtime, as
FTP does, declares the span it may land in rather than one port. Memona applies
these declarations automatically to each connection. At most 16 spans.
Limits and compatibility
| Item | Limit |
|---|---|
| Compressed package | 32 MiB |
| Expanded archive total | 128 MiB |
| WASM component | 64 MiB |
| Manifest | 64 KiB |
| README or license | 256 KiB each |
| PNG icon | 1 MiB |
The same validation runs on the store and in Memona. Components must implement
the full provider interface, including optional-operation
methods that may return unsupported, and import only supported host services.
Each published ID/version permanently identifies one byte sequence and SHA-256 digest. Correct a release by incrementing its version. Withdrawing a release prevents new catalog installations; it does not silently remove installed code.
Filesystem behavior
The generated reference lists exact names and types. This page explains the behavior expected across different remote services.
Connection and paths
Memona creates one component instance per connection. open receives the
provider ID and that connection’s configuration and returns its capabilities.
The host owns connection IDs; the plugin receives connection-relative paths.
/ is the connection root. Paths use / separators, cannot traverse above the
root and must never be interpreted as local OS paths. File names are returned
as names, not HTML. A listing is paginated with an opaque cursor and a bounded
limit. Do not reuse a cursor across unrelated listing requests.
Read and revisions
stat identifies a file or directory and may return an opaque revision token.
read takes an offset and maximum length and returns bytes, an optional revision
and an end-of-file flag. Use bounded range reads rather than loading an entire
remote file into guest memory. Do not silently truncate data while claiming EOF.
Revision values are provider-defined. They could be a service ETag or an immutable content identifier. Consumers compare them for equality; they must not parse them as timestamps or assume they are ordered.
Write and conflict handling
The initial write operation replaces a complete file within the host’s transfer limit. It is not an append or streaming-write operation. Large-file publishing must not be simulated by repeated calls that accidentally overwrite each other.
The caller chooses one of three conditions:
create-only: fail if the target already exists.if-match(revision): replace only that revision atomically.overwrite: replacement explicitly requested without a revision condition.
Only advertise conditional-write support if the remote service can enforce it.
A read followed by an unconditional write is not an atomic conditional write.
Return unsupported when you cannot honor the requested condition.
If a connection fails after a write or other mutation may have reached the
server, report outcome-unknown. Do not retry automatically: the operation might
already have succeeded. Consumers can inspect the remote state before deciding
what to do next.
Read-only stores return unsupported for mutations. They still implement all exported methods so the component has a predictable shape. Write permission from the user never overrides a provider’s missing capability.
Changes and cleanup
Providers that support change polling advertise that capability. A poll returns a bounded batch and optional cursor; it must finish promptly. The initial API exposes explicit poll calls. A caller retains its cursor and decides when to poll again; there is no automatic watcher subscription in this version.
close releases provider-owned connection state. Timeouts, disable, disconnect,
update or uninstall may terminate the instance even if cleanup cannot finish.
Never rely on an eventual close callback to commit a pending write.
Errors
Return the closest typed error category. Host/UI messages are generic and localized. Do not put credentials into log messages. An unsupported operation, permission denial, missing file and version conflict are different outcomes; turning all of them into an internal error prevents useful recovery.
Permissions and lifecycle
Network and system services
The host provides bounded HTTP requests through the WIT host interface. Network access permits public, LAN, VPN, link-local and loopback addresses. Socket ports must be declared in the package. Redirects follow the same transport rules. A provider must not assume that compiling a native networking crate grants socket or process access.
WASI support is deliberately limited. The plugin does not inherit Memona’s environment variables, local directories, credentials for other connections or subprocess access. Clock/randomness and standard-library plumbing are supplied only as permitted by the host. Filesystem imports do not imply that local host directories are mounted into the component.
Keep declared socket ports as narrow as the service allows. Do not include tokens in URLs or diagnostics; use the connection’s secret fields.
Agent grants
Each connection is off, read-only, or read/write for Memona’s remote_files
tool. The Rust host checks
the current setting before discovery and every operation. Plugins cannot raise
their own grants, and an agent’s permissive CLI mode cannot bypass them.
These controls do not sandbox the external agent’s independently available shell. An agent must use Memona’s tool to access a provider; virtual paths are not automatically mounted as system drives.
Resource limits and cancellation
Each guest instance has a 64 MiB linear-memory limit and a bounded execution budget. A plugin that loops forever or traps is stopped. Transfer sizes, queued work and operation duration are also bounded. Host I/O is asynchronous, but the WASI 0.2 guest interface can look synchronous to its Rust implementation.
Package validation and compilation use separate, short-lived workers with a 30-second deadline per job. The app shares a two-worker limit across both stages; the store permits two validation workers. Cancelling either stage kills and reaps its worker before the slot becomes available again. Only output from Memona’s own compiler worker can be loaded as compiled code; downloaded packages contain portable WASM components.
Worker memory is separate from the guest limit. Linux workers have a 2 GiB virtual-address-space ceiling; macOS workers receive 2 GiB beyond their startup mappings. Windows workers have a 2 GiB committed-memory ceiling. These are process allocation limits, not an identical resident-memory measurement on every OS.
Disabling or removing a plugin prevents new calls and cancels active work. Connection instances do not share mutable guest state or credentials. The backend may remain alive after windows close; all work ends when the backend itself exits.
Future frontend modules
A future Web Component runs in a browser context and has a different permission boundary. Serving JavaScript from a WASM component does not place that JavaScript inside the WASM sandbox. Version 1 does not execute frontend plugin modules.
Publish a release
- Open Account and enter your email address.
- Enter the 16-character sign-in code from your email to sign in.
- Claim a publisher name and prepare a package with that publisher in its ID.
- Upload your
.memona-pluginpackage in the publisher dashboard. - After automatic validation succeeds, the release appears in the public catalog.
Email is delivered through Resend. Codes expire after 15 minutes, have a limited number of attempts and can only be consumed once. Entering the code confirms sign-in; email scanners cannot consume it by following a link.
Signing in does not require a publisher name. Readers can like public plugins with a verified account and claim a publisher name later when they want to publish. Each account contributes at most one like to a plugin; clicking an active like removes it.
Public profile
Your publisher name is permanent and forms the first part of each plugin ID. After claiming it, edit your display name, biography and optional website in Account. Display names allow up to 80 characters, biographies 2,000 and website URLs 2,048. Websites must use HTTP or HTTPS.
Your public page at /publishers/your-publisher shows these fields, your joined
date and public plugins. Your email address and account ID stay private. A
plugin with no available release does not appear among your public plugins.
What validation checks
The store checks account ownership, archive structure and limits, the manifest, component validity, supported imports and the filesystem interface. It never runs a publisher’s component during validation. Memona repeats package and digest validation when installing.
Automatic validation is not a source-code review. Email verification confirms control of an address; it does not certify a developer’s identity or behavior.
Releases and updates
Versions are immutable. Upload a new semantic version for a fix, even if the old version has been withdrawn. Users select and install releases inside Memona’s Plugins dialog; website links do not automatically install code.
Describe connection requirements and supported operations clearly. Include license information and a README in the package. Do not embed private service credentials, publisher sessions or verification tokens.
Keep API compatibility separate from your plugin’s release number. A new plugin release can continue using the same Memona WIT API. A component requiring an unsupported API cannot be installed until Memona supports that contract.
Downloads and activity
Plugin pages show lifetime downloads and daily UTC activity. A download means an accepted package GET request whose artifact was successfully opened. It does not prove that the transfer finished or that Memona installed the plugin, and repeated requests from the same person count separately. HEAD requests, unavailable artifacts and withdrawn downloads do not increment the counters.
Lifetime and daily counters update together. Existing lifetime totals remain when daily tracking begins. Dates before tracking began are marked Not tracked; tracked dates with no requests show zero. The history endpoint supports 1–365 days and reports when tracking began. No historical downloads are invented to fill earlier dates.
World filesystem
Memona filesystem extension ABI 0.1.0, distributed as a WebAssembly component. Rust guests can target wasm32-wasip2. Selected WASI 0.2 CLI, clocks, random, IO and filesystem interfaces are allowed for the standard library, with empty arguments/environment, empty preopens, and discarded stdio. WASI sockets and HTTP imports are not allowed; networking uses the host interface.
- Imports:
- interface
memona:filesystem/host@0.1.0
- interface
- Exports:
- interface
memona:filesystem/provider@0.1.0
- interface
Import interface memona:filesystem/host@0.1.0
Host services scoped to the current connection. No ambient filesystem, environment, sockets, credentials, or network permissions are inherited.
Types
enum host-error
Stable failures without backend messages or credential-bearing URLs.
Enum Cases
enum log-level
Severity of a diagnostic event. The host may discard message contents.
Enum Cases
enum method
Every method the host will send. A verb outside this list cannot be spelled, so no plugin can smuggle one past the transport rules.
Enum Cases
record header
An HTTP header. Host-controlled transport headers cannot be overridden.
Record Fields
record request
Request head. At most 64 headers of 8 KiB each; the body is streamed.
Record Fields
record response
Response head. The body is read from the exchange that produced it.
Record Fields
status:u16headers: list<header>
resource exchange
One HTTP request and its response, streamed in bounded chunks so a transfer is never held whole in memory. A body of any size crosses in pieces, which is what multipart uploads of at least 5 MiB require. Dropping the handle abandons the request.
resource tcp
One open TCP connection. Dropping the handle closes the socket, and the host closes every socket when the connection is disposed.
resource udp
One datagram socket connected to a single peer.
variant setting
A configuration value, typed as the package’s manifest declared it. A secret is never logged by the host and never leaves the connection.
Variant Cases
Functions
[method]exchange.push: func
Appends at most 1,048,576 bytes to the request body. Every push
must land before respond.
Params
self: borrow<exchange>data: list<u8>
Return values
- result<_,
host-error>
[method]exchange.respond: func
Ends the request body and waits for the response head. Calling it twice is invalid-input.
Params
self: borrow<exchange>
Return values
- result<
response,host-error>
[method]exchange.pull: func
Reads at most 1,048,576 bytes of the response body, after
respond. An empty result means the body is complete.
Params
self: borrow<exchange>limit:u32
Return values
- result<list<
u8>,host-error>
http: func
Begins a request to any HTTP or HTTPS host, including LAN and loopback addresses. Each redirect and DNS result is checked, and DNS is pinned. Cross-origin redirects remove request headers; HTTPS cannot downgrade. The host bounds total bytes per exchange, not buffer size.
Params
request:request
Return values
- result<own<
exchange>,host-error>
[method]tcp.read: func
Reads at most limit bytes, between 1 and 1,048,576. An empty
result means the peer closed its side, not that data is pending.
Params
self: borrow<tcp>limit:u32
Return values
- result<list<
u8>,host-error>
[method]tcp.write: func
Writes at most 1,048,576 bytes and returns how many were accepted.
Params
self: borrow<tcp>data: list<u8>
Return values
- result<
u32,host-error>
connect: func
Opens a socket to any host, including LAN and loopback addresses, on a package-declared port. The host resolves and pins DNS and starts TLS when asked. Ports negotiated at runtime, such as an FTP data channel, must be declared.
Params
Return values
- result<own<
tcp>,host-error>
[method]udp.send: func
Sends one datagram of at most 65,507 bytes.
Params
self: borrow<udp>data: list<u8>
Return values
- result<
u32,host-error>
[method]udp.receive: func
Receives one datagram of at most limit bytes. An empty result
means none arrived inside the host’s wait, not that the peer left.
Params
self: borrow<udp>limit:u32
Return values
- result<list<
u8>,host-error>
bind: func
Connects a datagram socket under the same port rules as connect, for
the few filesystem protocols that still need one.
Params
Return values
- result<own<
udp>,host-error>
config: func
Reads configuration or a session-only credential for this connection.
Params
Return values
- option<
setting>
log: func
Emits a diagnostic event. Never include secrets or private document data.
Params
level:log-levelmessage:string
Export interface memona:filesystem/provider@0.1.0
Types
type setting
enum error-code
Stable operation failures. A mutation interrupted after dispatch can report outcome-unknown: inspect remote state before deciding to retry.
Enum Cases
enum entry-kind
Anything that is neither a file nor a directory, such as a link, is reported as other rather than flattened into one of the two.
Enum Cases
type revision
string
An opaque version token, at most 4096 UTF-8 bytes.
variant locator
How one resource is named: a path within the connection root.
Variant Cases
record capabilities
Optional operations. False capabilities must return unsupported. Conditional-write promises atomic create-only and revision comparisons. Virtual-directories says a directory is only ever a prefix of something stored, as in a flat object store: the host then never offers to create an empty one, warns that removing the last child removes the folder, and does not call a vanished folder an error. Such a provider may still accept mkdir by writing a marker, and must answer unsupported when asked to rename a directory.
Record Fields
write:boolmkdir:boolrename:boolremove:boolconditional-write:boolwatch:boolvirtual-directories:bool
record file-info
Byte length and an opaque revision, each if known.
Record Fields
kind:entry-kindsize:u64revision: option<revision>
record entry
One child name, without slashes, dot segments, or control characters.
Record Fields
name:stringinfo:file-info
record list-page
At most the requested number of entries. A cursor (maximum 4096 bytes) identifies another page; absence means enumeration is complete.
Record Fields
entries: list<entry>cursor: option<string>
variant write-condition
Atomic preconditions: absent file, exact opaque revision, or unconditional replacement. Providers must never simulate comparisons non-atomically.
Variant Cases
create-onlyif-match:revisionoverwrite
enum change-kind
Changes since a previous polling cursor.
Enum Cases
record change
A changed resource path relative to the connection root.
Record Fields
path:stringkind:change-kind
record change-page
At most 256 changes and a cursor of at most 4096 UTF-8 bytes.
Record Fields
changes: list<change>cursor: option<string>
resource reader
One transfer being read. The handle survives between host calls, so a file of any size is read as a series of separately bounded calls. Dropping it releases whatever the provider held open.
resource writer
One transfer being written. Dropping the handle before it finishes abandons the transfer, and the provider must leave no partial file.
Functions
[method]reader.pull: func
Reads onward from the last position, at most limit bytes, with
limit between 1 and 1,048,576. An empty result means end of file.
Params
self: borrow<reader>limit:u32
Return values
- result<list<
u8>,error-code>
[method]reader.revision: func
The revision this transfer is reading, once the provider knows it. A provider that sees the revision change mid-transfer must fail the next pull with conflict rather than mix two versions.
Params
self: borrow<reader>
Return values
- option<
revision>
[method]writer.push: func
Appends at most 1,048,576 bytes. Oversized pushes are rejected, never truncated.
Params
self: borrow<writer>data: list<u8>
Return values
- result<_,
error-code>
[method]writer.finish: func
Commits the transfer and evaluates the write condition now, against the state the caller last saw. A none result means the commit needs another call: keep calling until it answers some, and treat an interruption in between as outcome-unknown.
Params
self: borrow<writer>
Return values
- result<option<
file-info>,error-code>
open: func
Opens exactly one host-owned connection per component instance. The host passes only this connection’s configuration and in-memory credentials.
Params
provider-id:stringconfig: list<(string,setting)>
Return values
- result<
capabilities,error-code>
close: func
Best-effort cleanup before disposal. The host may dispose immediately after a trap, cancellation, permission change, or process shutdown.
stat: func
Reads metadata without changing the resource.
Params
target:locator
Return values
- result<
file-info,error-code>
list: func
Enumerates 1-256 direct children per request, without recursive traversal.
Params
target:locatorcursor: option<string>limit:u32
Return values
- result<
list-page,error-code>
open-read: func
Begins reading at a byte offset. Streaming, rather than one capped read, is what lets a provider serve a file larger than a single call allows.
Params
target:locatoroffset:u64
Return values
- result<own<
reader>,error-code>
open-write: func
Begins replacing one complete file if the write capability is present. The size hint, when the caller knows it, lets a provider choose between a single request and a multipart upload before the first byte.
Params
target:locatorcondition:write-conditionsize-hint: option<u64>
Return values
- result<own<
writer>,error-code>
mkdir: func
Creates one directory if the mkdir capability is present.
Params
target:locator
Return values
- result<
file-info,error-code>
rename: func
Renames within this connection if the rename capability is present.
Params
target:locatordestination:string
Return values
- result<
file-info,error-code>
remove: func
Removes one file or empty directory if the remove capability is present.
Params
target:locator
Return values
- result<_,
error-code>
poll: func
Polls for a bounded change page if the watch capability is present. No background callback or persistent subscription is installed by the host.
Params
Return values
- result<
change-page,error-code>