Build a plugin.
Teach Revive a new job.
A plugin is a manifest and a little JavaScript. It runs in Revive's sandbox, talks only through revive.*, and shows its results on the notch and in Revive's own cards.
Who can build
Plugins are in beta. Anyone can build and test one on their own PC in developer mode; publishing to the store is by invite while we review the first plugins closely.
- Write a manifest.json: who it is, what it may do, where it shows up.
- Write main.js with a handler for each hook.
- Test it on real files with plugin-test, or in Revive with developer mode on.
- Pack it with plugin-pack and fix every error and warning.
- Submit it. The store reviews it, signs it and lists it.
The manifest
manifest.json sits next to main.js (and an optional icon.png). Its permissions and data_use are exactly what the install card shows.
{
"id": "yourname.price-check",
"name": "Price Check",
"version": "0.1.0",
"publisher": "Your Name",
"description": "Drop a product photo on the wheel: see its price on Example Shop.",
"price": 0,
"data_use": "Sends the photo you drop to Revive's AI to name the product, then the product's name to api.example-shop.com (on your own key) to look up its price.",
"secrets": [
{ "id": "shop_key", "label": "Example Shop API key", "help_url": "https://example-shop.com/developers" }
],
"permissions": {
"net": ["api.example-shop.com"],
"ai": ["vision"],
"files": "dropped",
"clipboard": true
},
"hooks": {
"wheel": [
{ "id": "check", "label": "Check price", "description": "Name the product and look up its price", "accepts": ["image"] }
],
"ask": []
}
}idReverse-domain id, letters, digits, dots, dashes: yourname.plugin-name. Never changes between versions.nameA short name, up to 40 characters.versionYour version, such as 1.0.0. Every new version is reviewed and signed again.publisherYou or your company, as people will see it.descriptionOne line for the install card and the store.price0 for free, otherwise the price in US dollars.data_usePlain words: what leaves the PC and where it goes. Required if the plugin uses the network or AI. Shown on the install card.secretsKeys the person brings (their own API key): id, label and a help_url. Kept in Windows Credential Manager, never in your code.permissions.netThe exact https hosts it may reach, such as api.example.com or *.example.com.permissions.aiRevive's AI it may use: "vision", "text", "search" (text answered with live Google Search).permissions.files"dropped" to read only the files given to it, or leave it out.permissions.clipboardtrue if it may put text on the clipboard.hooks.wheelOptions on the format wheel (and the shelf) for dropped files: id, label, description, accepts ("image", "pdf", "any").hooks.askTools Revive's Ask and voice can call, in the same shape.The code
Calls are synchronous and throw on failure, so wrap each one and finish politely. This example matches the manifest above.
// main.js: plain, readable JavaScript (no eval, no minified blobs).
revive.on("check", ({ files }) => {
const photo = files && files[0];
if (!photo) return revive.notch.done("Drop a product photo first", false);
revive.notch.progress("Looking at the photo", 20);
let product;
try {
product = revive.ai.vision(photo, "Name the product in this photo as a shop would list it.").text.trim();
} catch (e) {
return revive.notch.done("Couldn't look at the photo", false);
}
revive.notch.progress("Checking the price", 60);
const key = revive.secrets.get("shop_key"); // null until the person adds their key
if (!key) return revive.notch.done("Add your Example Shop key first", false);
let r;
try {
r = revive.net.fetch("https://api.example-shop.com/v1/search?q=" + encodeURIComponent(product), {
headers: { Authorization: "Bearer " + key },
});
} catch (e) {
return revive.notch.done("Couldn't reach Example Shop", false);
}
const item = r.json && r.json.items && r.json.items[0];
if (!item) return revive.notch.done("No price found for " + product, false);
revive.notch.done("Found a price", true);
revive.card({
title: product,
subtitle: "Found at Example Shop just now · prices change",
rows: [{ label: "Example Shop", value: item.price, url: item.url }],
buttons: [{ label: "Copy name", copy: product }],
});
return { product, price: item.price };
});The revive API
The plugin's only door. Each call is checked against the manifest's permissions.
revive.on(hook, fn)Runs fn when a hook from the manifest is used. Wheel hooks get { files }. Return a small result, or nothing.revive.notch.progress(title, pct)Shows what it's doing on the notch, with the edge glow.revive.notch.done(title, ok)Finishes the run on the notch: ok true for done, false for a polite failure.revive.card({ title, subtitle, rows, buttons })Describes a result card; Revive draws it in its own style. Rows: label, value, url. Buttons: label with url or copy.revive.net.fetch(url, { method, headers, body })https GET or POST to a listed host only. Returns { status, text, json }. No redirects.revive.ai.vision(file, prompt)Asks Revive's AI about a file it was given. Returns { text }.revive.ai.text(prompt, { search: true })Asks Revive's AI; with search: true it answers using live Google Search. Returns { text }.revive.files.read(file)Reads a file it was given for this run, and only those. Returns { name, size, mime, base64 }.revive.storage.get(key) / set(key, value)A private 1 MB store for this plugin, kept across updates, deleted on uninstall.revive.secrets.get(id)The person's own key for a secret declared in the manifest, or null if they haven't added it.revive.clipboard.write(text)Puts text on the clipboard (needs permissions.clipboard).revive.open(url)Opens one of the plugin's own listed sites in the browser.Test and pack
Revive has a headless mode for developers. Run a hook on real files, then pack the folder into a .reviveplugin.
:: Run one hook of an unpacked plugin folder on real files
revive --headless plugin-test <dir> <hook> [files]
revive --headless plugin-test .\price-check check .\photo.jpg
:: Lint the code and manifest, then pack it
revive --headless plugin-pack <dir> <out.reviveplugin>
revive --headless plugin-pack .\price-check price-check.revivepluginPacked files you make yourself are unsigned: Revive only installs them with developer mode on, clearly marked. The store signs the reviewed version.
Security policy (beta)
Revive fixes people's PCs, so its plugins must be safer than anything else they install. Most of this isn't a promise from the developer: Revive enforces it in code, and the store reviews the rest before a plugin is signed.
What Revive enforces
What developers must follow
- Ask for the least. List only the sites, AI and permissions the plugin uses. The packer warns about permissions the code never uses; the store rejects them.
- Say plainly what leaves the PC. data_use names what is sent and where: "Sends the photo you drop to Google Lens (via SerpApi) to find the product." No vague words like "may share data with partners".
- No tracking. No analytics, fingerprinting, ad SDKs or calls home that the person didn't ask for. A plugin's network use must be its feature.
- No keys in code, ever. Use secrets. If your service needs a shared key, run your own small server and list it in permissions.net.
- Readable code. Ship bundled but not obfuscated JavaScript. No eval, no new Function, no encoded blobs. The store reviews the exact code it signs.
- Honest results. Show where every result came from (a link on the card) and when. Never say "guaranteed", "best", "virus-free" or "speeds up your PC" unless it's measured and true. Prices change: say "found at … just now".
- Respect sites' terms. Use official APIs. No scraping sites that forbid it, no bypassing logins, captchas or paywalls.
- Fail politely. Catch errors and finish with revive.notch.done("Couldn't …", false). Never loop on retries.
- Small and quick. A run should usually finish in seconds. Show progress with revive.notch.progress if it takes longer.
- People's data is theirs. Don't keep dropped files or results beyond what the feature needs. Don't send personal data (names, emails, files) anywhere data_use doesn't name.
- Paid plugins are fair. A clear price, no dark patterns, cancelling as easy as buying, and the plugin keeps working for what was paid.
If you build with AI (vibe coding)
AI-written plugins are welcome. They're held to the same rules, and these catch most of what AI gets wrong.
- Read every line before you pack. You're the publisher, not the AI.
- Never paste a real key into the chat or the code. Use secrets; the packer blocks keys it can find.
- Check the permissions it wrote. AI tends to add sites "just in case". Remove every site the code doesn't fetch.
- Check every URL it fetches. Make sure each one is an official API on a site you named in data_use, not a random mirror or scraper.
- No eval, new Function, minified blobs or "obfuscation for protection". Ask the AI to remove them.
- Ask it for error handling on every revive.* call, and test what happens offline, with a bad key, and with an odd file.
- Test in developer mode on real files, then run revive --headless plugin-pack and fix every error and warning.
- Don't let it invent claims. Read the card text it wrote; cut anything you can't show is true.
How a plugin gets to people
From your folder to someone's notch, with a review and a signature in between.
- Build and test with developer mode on (Revive Settings).
- Run revive --headless plugin-pack <folder> <out.reviveplugin>: the packer lints the code and the manifest.
- Submit it to the store. Review covers the code, the permissions against what the code does, data_use, claims, and a test run in Windows Sandbox.
- The store signs it with the store key and lists it. People download it and drop it on the notch.
- Every update is reviewed and signed again. Problems found later mean revocation: the plugin is turned off on every PC within hours.
Found a plugin breaking these rules, or a way around the sandbox? Email hello@revive.example. Sandbox escapes are fixed first.