Skip to content

storage plugin

Alexander Herling edited this page Oct 12, 2026 · 7 revisions

Writing an upload destination

An upload destination takes finished packages somewhere else — a server, a cloud store — as the last step of post-processing. put uploads one file and returns the destination's own identifier for it; verify asks, separately, whether the destination really holds it.

Scaffold rdownloader plugin new --type storage
plugin_type storage
World storage-plugin
Exports put, verify
Template sdk/templates/storage
Contract Interface storage

What the host promises

  • Nothing local is deleted until verify says the destination holds the object. That is why the check is a call of its own: an upload that returned success and lost the file would otherwise take the only copy with it.
  • You read the package through a handle, never a path, and only the file the job names. A file inside one of the package's folders is named by its relative path (Film/film.mkv), and the destination is expected to keep that path, creating the folders it needs.
  • A password never reaches you. Credentials come from the stored remote logins, matched on the destination's host and port; the password reaches the request as {{secret}}.

Permissions

The scaffold asks for net_http with *, because where somebody runs their server is not knowable in advance. It is narrower than it looks: what applies to an upload is the host of the destination that upload is for, and nothing else. A plugin for one hosted service names that service's hosts instead. source is granted by the type, and a storage plugin alone may send PUT, MKCOL, PROPFIND and DELETE. net_stream is available for a destination that is not HTTP.

[extension] claims stays empty: a destination is chosen by name in the post-processing settings.

Ask for exactly what you use: the install preview shows every grant, and a component importing an interface its manifest does not grant cannot be instantiated. Every capability is listed under Capabilities.

Rules worth knowing first

verify answers false for a missing object; an error is for a check that could not be made. source.progress(done, total) moves the upload's percentage; report per chunk and the host throttles it.

Step by step

1. Scaffold it

plugin new reads its templates from sdk/templates, so run it from a checkout of the repository:

git clone https://github.com/degoya/rDownloader.git
cd rDownloader
rdownloader plugin new --type storage --out ../myuploads
cd ../myuploads

Without an installation of rDownloader, the packaging tool rd-pack — cargo build --profile release-test -p rd-pack in the checkout — takes the same arguments: target/release-test/rd-pack plugin new --type storage --out ../myuploads.

The directory name becomes the crate name and the plugin's slug (--name sets another). The scaffold gets its own plugin id, its own copy of the contract in wit/rdownloader.wit and its own Ed25519 key pair: plugin-signing.key, whose public half is already in manifest.toml. Keep that file out of version control.

2. Make it yours

  • src/lib.rs holds the destination, the checkpoint and the reading of answers; the scaffold uploads in resumable chunks — open, send each chunk at its offset, complete.
  • src/guest.rs holds put and verify.
  • manifest.toml: [capabilities.net_http] domains, [limits] timeout_milliseconds.
  • locales/en.json: the English text of every failure code you report.

Change description and author under [metadata], and read the comment at the top of src/lib.rs: it states what the host promises this type.

3. Test it

cargo test

The unit tests need no WebAssembly toolchain: only src/guest.rs is wasm32-specific. Give every answer of your provider you read a test of its own.

4. Build the component

cargo build --release --target wasm32-unknown-unknown
wasm-tools component new target/wasm32-unknown-unknown/release/myuploads.wasm -o target/myuploads.wasm

This needs the wasm32-unknown-unknown target and wasm-tools 1.261.0, the version the bundled plugins are made with: cargo build makes a core module that carries the world src/guest.rs generates, and wasm-tools component new turns it into the component. The plugin is built without WASI: a component importing anything outside rdownloader:plugin is refused — see Building without WASI.

5. Package and sign it

rdownloader plugin package --manifest manifest.toml \
  --component target/myuploads.wasm \
  --locales locales --key plugin-signing.key --output myuploads.rdplug
rdownloader plugin conformance myuploads.rdplug --json

conformance answers whether this application would run the package; it has to be clean before you publish. For a key that outlives the scaffold, rdownloader plugin keygen --role plugin --output ./keys writes a pair: the Base64 in the .pub file goes into public_key, and key_id gets a name of your own. In CI, RDOWNLOADER_PLUGIN_SIGNING_KEY holding the private key's PEM text replaces --key. Details: Packaging, signing and distribution.

6. Install and trust it

For a first try without signing, package with --development instead of --key and start rdownloader serve --plugin-development-mode, the only mode that accepts an unsigned package.

Install a signed package under Settings → Plugins → Add as an upload, or with rdownloader plugin install myuploads.rdplug --root data/plugins. Review before installing shows every permission and the signing key; a key this installation has not seen asks to be trusted — compare its fingerprint. The message after installing says whether the plugin runs at once or after the next restart; see Installing and trust.

Set the upload target under Settings → Post-processing → Scripts & upload to plugin:<plugin-id>/<destination> — Use a plugin destination next to the field fills it in; everything after the first slash is your plugin's own vocabulary. See Post-processing.

7. Publish it

Publish the .rdplug together with your key's fingerprint, or — so that people get your new versions in the Catalogue — in a plugin repository of your own: a signed index built with rdownloader plugin index build and a repository-role key. A repository only delivers: every package in it still needs your plugin key, approved by the person installing it. See A repository index and Running a plugin repository.

The SDK's reusable CI workflow, sdk/ci/plugin.yml, builds, checks the imports, packages and runs conformance on every push and pull request; it signs only on a push to the default branch or a tag, so a pull request never sees the key.

Read next

Clone this wiki locally