Skip to content

developing a plugin

Alexander Herling edited this page Oct 5, 2026 · 13 revisions

Developing a plugin

rdownloader plugin new scaffolds a plugin that builds, packages and passes conformance before you change a line of it — so the first failure you see is about your code, not about the setup.

What you need

Tool Version
Rust 1.99, Edition 2024
cargo-component 0.21.1 — the version the bundled plugins are built with
Target wasm32-unknown-unknown (rustup target add)
wasm-tools For the import check

Scaffolding

rdownloader plugin new --type resolver --out ./myhoster
cd myhoster
cargo component build --release --target wasm32-unknown-unknown

--type takes every world of the contract, thirteen in all, and each has a page of its own under Plugin development that goes from the scaffold to a published package:

--type What it does
resolver Turns a hoster link into a downloadable address and checks links
transfer Carries the bytes of a protocol the application does not know
intake Turns text and URLs into LinkGrabber candidates
intake-mirrors An intake parser that also states every source of a file
crawler Turns one address into the files behind it
remote-job Runs a job that lives at a provider and outlives the call
auth Signs an account in through the provider's own flow
oauth Signs an account in by OAuth and renews the token
enricher Adds metadata to a link
notifier Delivers notifications to one more destination
postprocess Runs one more step after a package has been downloaded
storage Uploads finished packages somewhere
stream-transform Answers with an address and how its bytes become a file

The directory name becomes the crate name and the manifest's provider slug; --name sets a different one.

plugin new reads its templates from sdk/templates, so run it from a checkout of the repository or from a directory holding a copy of that folder. A release binary on its own does not carry them.

The scaffold brings its own copy of the WIT contract, rdownloader:plugin@0.10.0, in wit/, so nothing about the build needs a checkout of rDownloader, and its own Ed25519 key pair in plugin-signing.key — which belongs in your secret store, not in version control. plugin new writes a .gitignore before the key, listing plugin-signing.key, target/ and *.rdplug, so a first git add . leaves the key out; keep those lines when you edit the file.

Building without WASI

cargo component build --release --target wasm32-unknown-unknown

cargo-component additionally needs the wasm32-wasip1 target for its own componentisation; the finished plugin nevertheless imports only the WIT host contract.

A component that imports anything outside rdownloader:plugin is refused — when it is packaged, and again when it is installed. That includes a wasi: interface pulled in by a dependency you did not choose directly.

scripts/check-plugin-imports.sh reads the import section with wasm-tools and names the offending interface. The CI workflow template runs the same check.

Capabilities

Each grant in [capabilities] is one WIT interface.

  • A component importing an interface its manifest does not grant is refused at packaging time and again at install time, so asking for less than you use fails at build time, not at a user's download.
  • Ask for exactly what you use. The plugin manager shows your grants to the person deciding whether to install you.
  • The file system, other processes, and the network beyond your declared domains are not capabilities that happen to be denied. There is no interface for them.

Translations

The scaffold ships locales/en.json. English is the one language a localised plugin must carry — a package with any locale file and no en.json is refused.

The interface is offered in German, English, Spanish and French, and the bundled plugins ship all four as de.json, en.json, es.json and fr.json. A language a plugin lacks falls back to English.

Every scaffold is a working example

Each scaffold is a small, working plugin of its type rather than a set of signatures: a PIN sign-in, a resumable chunked upload, a CRC-32 over a package, a bounded folder walk, a key read out of a link's fragment. The promises the host makes to the type are written at the top of src/lib.rs; they are what shapes the code, so read them before changing it.

Everything that can run without a WebAssembly toolchain lives outside the component, and src/guest.rs — the translation into the contract's types — exists only on wasm32, so cargo test runs a fresh scaffold's unit tests straight away:

rdownloader plugin new --type oauth --out ./myprovider
cd myprovider && cargo test

Every scaffold carries a README.md naming its files and the rules worth knowing first. The templates themselves are in sdk/templates.

Conformance

rdownloader plugin conformance myhoster.rdplug --json

Answers machine-readably whether this application would run your package. Run it before you publish anything.

Errors and user-facing text

User-facing text never lives in the plugin as prose. It lives as a stable code, translated in the plugin's own catalogue — the same rule the application follows.

An error value a provider wrote that is not code-shaped is dropped whole rather than filtered: filtering keeps the digits of a leaked credential.

Read an HTTP status nothing else explains the way the bundled plugins do, so a person sees the same outcome whichever plugin served the link:

Status Category Why
401, 403 account-invalid — or a plain HTTP error (permanent) when the request carried no account A bot wall must not send anybody to fix credentials they never gave
404, 410 permanent The file is gone for good; it is not retried. A link check still reports it offline
451 offline Withheld for legal reasons, often regionally; it is retried
429 rate-limited, with the provider's Retry-After
5xx transient, with the provider's Retry-After where one was sent

Take Retry-After in seconds, at most one day — the host holds every wait to a day anyway — and read Retry-After: 0 as your own default wait, not as "retry at once".

Testing against a running service

rdownloader serve --plugin-development-mode

accepts unsigned packages. Package with --development to produce one. Local development only.

Related pages

Reference in the repository: sdk/README.md · rdownloader.wit, the contract

Clone this wiki locally