# Plugins

> Connected integrations that own an external surface and register their own tools — the bundled Google trio, the signed in-binary catalog, the HTTP and MCP rails, OAuth and api_key auth, and the commands that manage them.

A plugin is a connected integration that owns an external surface — an API, a vault, a SaaS account — and registers its own tools into Fermix's [capability registry](/docs/capabilities-and-tools). Each tool a plugin contributes is named `<plugin>_<tool>` (for example `gmail_send_message` or `slack_list_channels`), so tools from different plugins cannot collide and the agent can see at a glance which surface a tool belongs to. Once a plugin is connected, the agent prefers its tools over the generic `browser`/`web_fetch`/`shell` capabilities for that surface.

## Bundled plugins

Three plugins are always present and cannot be uninstalled (you disable them instead):

- **Gmail**
- **Google Calendar**
- **Google Drive**

All three authenticate through Google OAuth. They follow a graceful-403 model: a connected plugin registers *all* of its tools regardless of which OAuth scopes you granted. If you call a tool whose scope was not granted, you get a clear "reauthorize" error telling you to reconnect with the missing scope — the tool does not silently vanish from the agent's view. Other Google 403s are classified distinctly (file-permission, organizer-only, rate-limit) so the error message says what actually failed instead of collapsing every denial into one generic auth error.

## Where additional plugins come from

Beyond the bundled trio, plugins come from a **signed catalog bundled inside the Fermix binary**. This is the only catalog source — there is no remote index and no runtime refresh. New and updated plugins reach you with the next Fermix release, so you get them by running `fermix upgrade` (see [distribution and upgrade](/docs/distribution-and-upgrade)).

Which plugins exist, and at which versions, is a moving target that this page deliberately does not duplicate. Run `fermix plugins catalog` to see what the binary you are running actually offers, and browse [`tezra-io/fermix-plugins`](https://github.com/tezra-io/fermix-plugins) for the source each one is built from.

Installing a plugin streams its tarball, verifies the artifact against the catalog's recorded **sha256** hash and a **cosign** signature, and re-validates the decoded manifest before activating it into a versioned store under `$FERMIX_HOME/plugins/`. The guarantee is simple: a plugin that installs will load. (Cosign verification needs the `cosign` binary on the daemon's `PATH`; a genuinely missing one is refused with an install hint, while one that exists but is not on the supervised daemon's `PATH` can surface as a misleading signature error — see [distribution and upgrade](/docs/distribution-and-upgrade).)

The catalog currently ships:

| Plugin | What it does | Rail | Auth |
|--------|--------------|------|------|
| **GitHub** | Work with repositories, issues, and pull requests. | HTTP | OAuth |
| **Notion** | Search, read, create, and update pages and data sources. | HTTP | OAuth |
| **X** | Post, reply, search, like, and read on X (Twitter). | HTTP | OAuth |
| **Slack** | Read channels, messages, threads, and members. | HTTP | api_key |
| **Discord** | Read servers, channels, messages, and members. | HTTP | api_key |
| **AgentMail** | Create agent-owned email inboxes; send, read, and reply to mail. | HTTP | api_key |
| **Obsidian** | Search, read, create, and append to notes in your local Obsidian vault. | MCP | none |
| **Computer Use** (`computer_use_sidecar`) | The native helper binary behind the computer-use capability — a setup card, not a tool set, and not managed by `fermix plugins` (see below). | — | none |

One catalog entry is special. **Computer Use** (`computer_use_sidecar`) is not a tool-registering plugin: it is the native helper binary behind the **experimental, off-by-default** computer-use capability (see [capabilities and tools](/docs/capabilities-and-tools)). It runs on Apple Silicon (M-series) macOS and Linux x86_64 only — on an Intel Mac the download fails with a clear "no build for this machine" error.

It is also the one entry that does not go through the signed-tarball pipeline above, and `fermix plugins` does not manage it. The helper is fetched from the [compux](https://github.com/tezra-io/compux) project's own signed release and verified against a SHA-256 checksum baked into your Fermix build, then cached under `~/.fermix/plugins/compux/`. Enabling its card on the setup **Plugins** page does both halves — downloads the helper and flips `[fermix_core.computer_use] enabled` — and both are needed, with the change applying on the next daemon restart.

You only do that once. After that, a daemon that has computer use enabled but is missing the helper matching its build — the state an upgrade lands in when a new release pins a newer helper — downloads it itself on its next start. That happens on daemon boot only, is bounded to about thirty seconds, and fails soft: if the download does not land, computer use simply stays off until the next restart or until you use the setup card again.

## The two rails

Every tool-registering catalog plugin runs on one of two execution rails (the Computer Use helper, which registers no tools, is the one entry with neither).

### HTTP rail

An **http** plugin is a set of declarative request templates that run **in-VM** — Fermix makes the HTTP calls itself, no external process. The rail is double-guarded:

- **Install time:** the templates are validated, and every endpoint must be `https://` with no placeholder or userinfo (`user:pass@`) host.
- **Runtime:** an SSRF floor blocks requests to loopback, private, and cloud-metadata addresses, so a template can never be steered at an internal host.

Responses are guarded too: only JSON is accepted, bodies are capped at 5 MiB, and multi-page listings are fetched through bounded pagination — large downloads belong on the MCP rail.

### MCP rail

An **mcp** plugin is a **supervised local process** the daemon launches and monitors. On connect Fermix discovers the process's tools and registers them as `<plugin>_<tool>`, exactly like the HTTP rail. Installing an mcp plugin first probes the host runtime its manifest declares (a Node or Python interpreter, or a vendored standalone binary); a missing or too-old runtime is refused with a `missing_host_runtime` status rather than a broken install.

Supervision is bounded, not infinite. A server that fails tool discovery is retried with exponential backoff — up to five attempts in total; if it never answers, it gives up terminally and is quarantined: the process tree is torn down and its tools stay unregistered instead of respawning forever. A genuine transport blip still reconnects on its own. Enabling or disabling a plugin asks the running daemon to re-apply its configuration, which starts a quarantined server fresh — as does restarting the daemon with `fermix restart`.

This rail is plugin-managed and distinct from the manually-configured `[mcp.servers]` blocks documented in [inbound and outbound MCP](/docs/mcp) — those are MCP servers you wire up by hand in `config.toml`; plugin mcp processes are installed and supervised through the plugin system.

## Authentication

An HTTP-rail plugin authenticates one of two ways. Either way, the credential is injected using the scheme the plugin's manifest declares for the `Authorization` header — `Bearer` by default, or another scheme such as `Bot` for a provider like Discord. (A plugin that needs no credential at all — like Obsidian, whose process reads a local vault — declares auth `none` and is ready as soon as it is enabled.)

| Auth mode | How you connect | Not-ready status |
|-----------|-----------------|------------------|
| **OAuth** | An OAuth provider client configured under `[fermix_core.oauth.<provider>]` (client id + secret), then `fermix plugins auth login NAME` — logging in also enables the plugin. | `needs_client_config` until the client is configured, then `needs_auth` until connected. |
| **api_key** | A static secret the operator sets with `fermix plugins auth set NAME VALUE` — stored in the OS keychain, never plaintext config. | `needs_secret` until set. |

In today's catalog, **GitHub**, **Notion**, and **X** connect over OAuth; **Slack**, **Discord**, and **AgentMail** use an api_key (a bot token or API key you create with that service). For more on how OAuth clients and keychained secrets are stored, see [auth and secrets](/docs/auth-and-secrets).

### When a credential stops working

A failing credential reaches the agent as a named, actionable tool error — never a bare status code — so it can tell you the exact fix (or, for an operator, run it):

- **OAuth token rejected** (HTTP 401): *"`<plugin>` needs reconnection. Run `fermix plugins auth reauthorize <plugin>`."*
- **API key rejected**: *"API key rejected — update it with `fermix plugins auth set <plugin>`"* (a missing key points at the same command).
- **Enabled but never connected**: *"`<plugin>` is enabled but not connected. Run `fermix plugins auth login <plugin>`."*

On an OAuth 401 Fermix first attempts one silent token refresh and retry — but only for tools the manifest marks read-only. A write that comes back 401 is never blindly retried; it surfaces the reauthorize error immediately.

Tokens can also go stale from disuse, without any tool call failing: a token well past its expiry is flagged before next use — as a **Reconnect needed** badge on the setup page and in `fermix doctor`'s `auth tokens` check (see [auth and secrets](/docs/auth-and-secrets)).

## Readiness states

When a plugin is enabled but not yet ready to use, the agent sees a `status=` marker in its prompt instead of the tool simply disappearing — so it can tell you what to fix rather than failing blindly:

| Status | Meaning | Fix |
|--------|---------|-----|
| `needs_client_config` | An OAuth plugin whose provider client (client id + secret under `[fermix_core.oauth.<provider>]`) is not set up. | Configure the OAuth client on the setup page. |
| `needs_auth` | An OAuth plugin that has not been connected. | `fermix plugins auth login NAME` (or connect it on the setup page). |
| `reauthorization_required` | An OAuth plugin whose stored token was invalidated by the provider. | `fermix plugins auth reauthorize NAME`. |
| `needs_secret` | An api_key plugin with no secret set. | `fermix plugins auth set NAME VALUE`. |
| `needs_config` | A manifest-declared config value is missing. | `fermix plugins config set NAME KEY VALUE`. |
| `missing_host_runtime` | An mcp plugin whose host runtime is missing or too old. | Install or upgrade the runtime, then `fermix plugins doctor NAME`. |
| `not_installed` | A catalog plugin that is enabled but not installed locally. | `fermix plugins install NAME` (or `enable` auto-installs it). |
| `incompatible` | An installed version outside the running Fermix's compatibility window (typically after `fermix upgrade`). | `fermix plugins upgrade NAME`, or upgrade Fermix. |

## How the agent discovers plugins

The agent learns what is live from the `## Plugins` section of its runtime prompt. **There is no agent tool that lists plugins** — a ready plugin's tool names are listed in that prompt section, and the agent calls them directly (with schemas resolved on demand through tool-schema deferral; see [capabilities and tools](/docs/capabilities-and-tools)). An enabled-but-not-ready plugin shows its `status=` there instead, which signals "not connected" — so the agent offers to connect it on the setup page rather than scraping the surface another way.

To inspect things yourself: `fermix plugins list` shows each plugin's status and connected account, and `fermix plugins installed` shows the local store. Note that `fermix plugins catalog` lists the plugins the registry already knows about (bundled, installed, and dev-local) — the full catalog, including plugins you have not installed yet, is shown on the web setup **Plugins** page. Enable, disable, and logout take effect on the agent's next turn (the reload drops cached runtime context — no daemon restart needed), and calling a disabled plugin's tool refuses with an enable hint rather than a generic auth error.

## Operator commands

The `fermix plugins` command group manages the whole lifecycle. The full signatures live in the [CLI reference](/docs/cli-reference); the key verbs are:

- **Inspect:** `list`, `catalog`, `installed`, `doctor`, `auth status [NAME]`
- **Toggle:** `enable`, `disable`, `reload`
- **Manage the local store:** `install`, `upgrade`, `pin`, `uninstall`, `gc`
- **OAuth auth:** `auth login NAME`, `auth reauthorize NAME`, `auth refresh NAME`, `auth logout NAME`
- **api_key auth:** `auth set NAME VALUE`, `auth clear NAME`
- **Per-plugin config:** `config NAME` (shows declared keys and values), `config set NAME KEY VALUE` (sets manifest-declared `[fermix_core.plugins.<name>]` values)

The web setup **Plugins** page renders the full catalog: install-on-connect cards that verify sha256 + cosign, with OAuth client id/secret entered through a per-provider modal. (The one exception is the **Computer Use** card, which installs the native helper via the sha256-verified download described above and shows readiness from the computer-use feature itself — "Ready" or "Needs setup".)

## For plugin authors

To develop a plugin without an install or tarball step, point Fermix at a local directory:

```toml
[fermix_core.plugins]
dev_local = "/path/to/fermix-plugins/plugins"
```

The path's immediate subdirectories are treated as plugin directories (for a `fermix-plugins` checkout, that is its `plugins/` directory). They load into the registry like installed plugins — full manifest validation, skills loaded in place — with no signing or packaging. A missing or unreadable path, a bad manifest, or a name that collides with a bundled or installed plugin fails plugin loading loudly rather than being skipped.
