Buoy plugins
Plugins let you teach Buoy about your CRDs (or extend its handling of built-in kinds) without forking the app. A plugin is one TypeScript file (.tsx). Drop it into the plugins directory and Buoy hot-reloads it across every open tab.
What plugins can do:
- Detail tabs — add a tab to the detail view for matching
(group, kind)pairs. The tab body is a Reactrender(ctx)callback that returns JSX built from Buoy’s widget catalog (<KeyValue>,<Table>,<Pills>,<Conditions>,<RelatedList>, …). Live cluster reads inside the tab useuseObject/useListhooks. - Actions — add menu items in the detail-view kebab and the list-view bulk actions menu. Each action’s
run(ctx)callback issues patches viactx.mergePatch/strategicPatch/jsonPatch/statusPatch/delete, gated by a confirm modal. - List columns — declare default
-L/-F/--anncolumns and promoted-wide columns for a kind, somycrdshows the right columns out of the box. - Slash commands — register
/<name>commands (buoy.addCommand) that do arbitrary async work: navigate, mutate, prompt the user, run background tasks. - Custom views — register
/<name>to open a full top-level view (buoy.addView), like the built-in/pluginsor/contexts. - Async helpers —
notify(msg)for toasts,runInBackground(label, fn)for spinners,prompt({fields})for modal inputs,useAsync(fn, deps)for one-shot async work inside render. See commands.md.
What plugins can’t do (intentionally):
- Replace or hide built-in tabs and actions. Plugins are purely additive — a Pod’s
Logstab always shows up, no matter what plugins are installed. - Mutate the cluster outside the patch / delete primitives. There’s no exec, port-forward, or arbitrary HTTP — if you need that, you’re outside the plugin model.
- Add Tauri commands or talk to non-Kubernetes services.
Contents
- Your first plugin — five-minute tour.
- Installation paths — where files live on disk; install from URL.
- Widget reference — every component, hook, and async helper.
- Actions reference — patch types, confirm gating.
- Slash commands + views —
/foocommands and/fooviews. - Examples — copy-paste-able plugins for common patterns.
Your first plugin
Scaffold from the command bar with /plugin new <name> (or Settings → Plugins → New TypeScript Plugin). The scaffold writes <name>.tsx plus buoy-sdk.d.ts and tsconfig.json into the plugins dir so any TS-aware editor gets full autocomplete with zero npm install.
A minimal plugin looks like this:
/// <reference path="./buoy-sdk.d.ts" />
export default (buoy: Buoy) => {
buoy.meta({
name: "configmap-data",
version: "0.1.0",
description: "Quick view of ConfigMap entries",
});
buoy.addDetailTab({
match: { group: "", kind: "ConfigMap" },
id: "data",
label: "Data",
render: ({ obj }) => {
const data = obj?.data ?? {};
const keys = Object.entries(data);
return (
<>
<KeyValue title="Summary">
<Row label="Entries" value={keys.length} />
<Row label="Binary" value={Object.keys(obj?.binaryData ?? {}).length} />
</KeyValue>
{keys.length === 0 && (
<Banner tone="warn" text="This ConfigMap is empty." />
)}
<Table
title="Entries"
rows={keys}
columns={[
{ header: "Key", cell: ([k]) => k },
{ header: "Bytes", cell: ([, v]) => (v as string).length },
]}
/>
</>
);
},
});
buoy.addAction({
match: { group: "", kind: "ConfigMap" },
id: "touch",
label: "Touch",
confirm: {
phrase: (obj) => obj.metadata.name,
title: (obj) => `Touch ${obj.metadata.name}`,
},
run: async (ctx) => {
await ctx.mergePatch({
metadata: {
annotations: { "example.com/touched-at": new Date().toISOString() },
},
});
},
});
};
A few things to notice:
match: { group: "", kind: "ConfigMap" }— empty group is the core API group.apps/argoproj.io/my.crd.ioall work the same way for other kinds.- The
render(ctx)callback is a regular React function — it receives{ obj, context, namespace, tick, openObject, openNamespace }and returns JSX. Anything you can write in TypeScript works here:if,.map(), destructuring, helper functions you define elsewhere in the same file. - Inside
render, you can calluseObject(...)anduseList(...)to embed live cluster reads. They’re real React hooks; their results re-render the tab as the underlying watch updates. - Actions are async callbacks.
ctx.mergePatch/strategicPatch/jsonPatch/statusPatch/deleteare pre-bound to the target object — you only supply the patch body. Confirm fields can be plain strings or(obj) => stringcallbacks (use a callback when the confirm needs object data, like the name to type).
Built-in plugins
Buoy ships with a small set of plugins embedded in the binary. They show up in the /plugins view with a Built-in badge alongside any user plugins you’ve installed:
- secret-decoded — adds a
Decodedtab to every Secret, base64-decoding each entry underdataand rendering as a block. Same access model as the YAML tab; just saves a copy/paste-and-decode dance.
Built-ins can’t be uninstalled. To replace one, write a user plugin with the same meta.name — yours wins because user plugins register later.
Installation paths
Plugins live under:
| OS | Path |
|---|---|
| macOS | ~/Library/Application Support/com.buoy.app/plugins/ |
| Linux | ~/.config/com.buoy.app/plugins/ |
| Windows | %APPDATA%\com.buoy.app\plugins\ |
You can install in three ways:
- Drop a
.tsxfile at the root of the plugins dir. The basename becomes the plugin’s id. - Drop a folder (e.g.
my-org-crds/plugin.tsx). Useful when a plugin ships with a README or example screenshots. /plugin install <https://url>from the Buoy command bar. The file is fetched and cached underplugins/.cache/. HTTPS only —http://andfile://are rejected because anything in the plugin dir can issue patches against your cluster.
The /plugins view (also reachable from Settings → Plugins) lists everything installed, shows compile errors inline, and offers uninstall / enable / disable toggles.
Hot reload
Buoy watches the plugins directory and re-compiles on every change. Edit a .tsx, save, look at the open detail view — your edits apply without restarting Buoy or refreshing the view. Compile errors surface in the /plugins view rather than silently dropping the plugin.
Sharing plugins
The simplest distribution is a public gist. Share the gist URL and recipients install with /plugin install <gist-raw-url>. For more polished distribution, host the .tsx in a repo and link the raw GitHub URL. Buoy doesn’t have a registry yet.
Security model
Plugin code runs on the main thread with the same trust level as Buoy itself — there is no sandbox. Plugins can do anything you could do with kubectl patch and kubectl delete against your active context (and as your current impersonation, if /as is active). They cannot exec, port-forward, read secrets they couldn’t already read, or talk to anything outside the kube API. Every mutation goes through Buoy’s standard ConfirmModal — the same red typing-required dialog the built-in Delete uses.
When installing from a URL there’s no signature check yet — install plugins from sources you trust, the same way you’d kubectl apply a manifest you found online.
Where to go next
- Widget reference — the visual primitives plugins compose from.
- Actions reference — patch shapes and confirm gating.
- Examples — full working plugins for common patterns.