# Extensions

> Add commands and guidance to a machine's node, such as Blender renders or a team's working rules, install one from a clone, and check it in node health.

An **extension** adds something to a machine's node: commands agents can ask that machine for
(render a Blender scene, grab a still from DaVinci Resolve), or guidance for agents' runs. What a
command makes is posted as an ordinary file, so the apps show it with nothing new to install.

## What an extension is

Each extension is a folder with an `extension.toml` that says what it is, what it can do, and what
it needs: the apps it drives, network access, a GPU, which vault secrets it may use. There are two
sorts you add to a node:

| Sort | What it does | Example |
|---|---|---|
| A **command** extension | Runs as its own process on the node, and does work agents ask for: renders, previews, stills. Long jobs report progress, can be cancelled, and pick up where they left off after a restart | Blender, DaVinci Resolve |
| A **behaviour** | Guidance for agents: a `SKILL.md` the node adds to the wake prompt of each agent it's switched on for. It runs no code | A team's rules for coding agents |

GitHub is connected to core instead, not to a node: see [Projects](/guides/projects/#connect-github).

Every table and field of the manifest is in the [`extension.toml` reference](/reference/extension-manifest/). To make one, see [Write an extension](/guides/write-an-extension/).

## Find an extension

Browse and search every published extension at
[pointman.ai/extensions](https://pointman.ai/extensions), or in the apps. Each has a page with its
README, its versions, what it can access, and a badge for who stands behind it: **Pointman** for
official ones, **✓ Verified** for a publisher Pointman has checked. To list your own, see
[Publish an extension](/guides/publish-an-extension/).

## Ask a machine to do something

Agents use two tools for every command extension:

```json
{
  "command": "blender.render",
  "params": { "file": "/Users/you/work/shop/hero.blend", "frames": "1-250" },
  "thread_id": 812
}
```

`run_on_machine` queues the command and returns a request number at once. The node runs it, and
posts the result in the thread you named. `request_status(id)` shows how it's going: its state, its
progress ("render: 120 of 250 frames, 52 s a frame, ~2 h left") and the file it published.

| Parameter | Meaning |
|---|---|
| `command` | One of the commands your nodes run. The tool's own description lists them, with their parameters |
| `params` | The command's parameters, checked before anything runs |
| `machine` | A machine running the extension now. Leave it out for the first one that's free |
| `thread_id` | Where to post the result |
| `split` | For `blender.render`: share the frames between every machine running Blender, and join them into one video at the end |

To split a render, share the `.blend` first with `share_file(..., as_is=True)` so every machine can
fetch it, and pass its `pointman://asset/…` link as the file. A faster machine takes more parts, and
one that goes away leaves its parts to the rest.

## Add an extension to a machine

The machine always has the last word. Its node installs only extensions named in its own
allowlist, `~/.config/pointman/node-allow.toml`:

```toml
extensions = ["blender", "resolve"]
```

With no list, every install from elsewhere is refused. Then add it in one of three ways.

**From the app** (owners and admins): open **Settings › Extensions**. Each row says what the
extension adds (commands, tools for agents, an MCP server, guidance, link pills, accounts), where it
runs ("On studio and linux-box", or "In core" for one that works with every machine off) and what
needs you, such as a sign-in or a machine where it's failing, which come first. **Add** puts it on a
machine that doesn't have it, and **Remove**, from its menu, takes it off one. **Sign In** picks the
machine for you, and an extension with accounts has **Add Account**.

**In the node's config:** list it in `~/.config/pointman/agent.toml`, then restart the node:

```toml
extensions = ["blender"]
```

**From the command line,** with `pointman add`: by name, from a GitHub repo, or from a folder you
have.

```bash
pointman add checklist                         # from your workspace's catalogue
pointman add 'checklist>=0.2,<1'               # within a range of versions, as uv writes them
pointman add acme/pointman-checklist           # a repo, brought into the catalogue (owners)
pointman add checklist --node linux-box        # on another machine's node
pointman add ~/code/pointman-checklist         # a folder, while you're writing one
```

Your core keeps a catalogue of extensions for your workspace, each released from its own
`pointman-<name>` repo on GitHub, checked and packed by core. `add` installs the version you asked
for from it, adds the id to `node-allow.toml`, and turns it on; a command extension starts within
about ten seconds. A name that isn't in the catalogue yet is brought in from its repo when an owner
adds it. With `--node`, the other machine's node does it, and its own `node-allow.toml` still
decides. `pointman upgrade` moves extensions to the newest version within their range and this
machine's node to the build your core runs; `pointman list` shows what's installed, and `pointman
remove <id>` stops one and takes it off the allowlist. See the [command line reference](/reference/cli/#pointman-add-upgrade-and-remove).

:::caution
Extensions run as your user on that machine, like the node itself. Add only ones you trust. The
node refuses any command that points an extension at files outside your work folder, your agents'
folders and its own, and hands it only the vault secrets its manifest names.
:::

## Give an extension what it needs

Some extensions need something from you before they work, such as an account's token. When you
add one, `pointman add` tells you, and the apps ask for it until it's there:

- At the top of the **Machines** tab, a row says what's missing, with an **Add** button.
- Or type `/tickets add` in any thread's reply box (with the extension's id in place of `tickets`).
  The value is never posted; the thread only gets a line saying it's in the vault.

Either opens a sheet with a label (when the extension takes more than one) and the value, in a
hidden field. An extension that can sign an account in itself also offers **Sign in**: it gives you
a sign-in page to open, on any device, and turns what that page shows into the token on the machine,
so nobody sees it. The value is sealed in the vault for your machines, like any
[secret](/guides/secrets/), and the extension gets it through the vault.

An extension asks for these in its `extension.toml`, one `[[credentials]]` table each:

```toml
[[credentials]]
id = "key"
title = "Tickets API key"
help = "Make one under Settings › API keys in your Tickets account."
item = "tickets.key"
value = "tk_[A-Za-z0-9]{32}"
placeholder = "tk_…"
```

| Field | Default | Meaning |
|---|---|---|
| `id` | required | What it is, within the extension |
| `title` | required | What the apps call it |
| `help` | none | Where to get the value, in a line or two |
| `item` | required | The vault item it's kept as. `tickets.{label}` asks for a label too, so you can add several. |
| `value` | any text | A regular expression the whole value must match |
| `placeholder` | none | A hint in the empty field |
| `needed` | `1` | How many the apps ask for |
| `sign_in` | `false` | `true`: the extension signs the account in itself, and the apps offer **Sign in** |

## Links as pills

An extension can make links to its service show as pills in posts, the way GitHub's do: an icon and
a short label instead of the whole address. Its manifest says what such a link looks like, one
`[[links]]` table each:

```toml
[[links]]
kind = "page"
match = "https://pages.example.com/@{owner}/{page}"
show = ["title"]
```

| Field | Meaning |
|---|---|
| `kind` | The service's name for it, such as `page` or `pull` |
| `match` | What the link looks like, with `{placeholders}` for its parts |
| `show` | What the pill shows, in order: `title`, `repo`, `number` (#42), `sha` (a commit's short hash), `state`, `checks` or `lines`. For any extension but GitHub, the title is the link's last part. |

The apps draw these while a node of yours runs the extension, with its icon, and remember them
between launches.

## Behaviours

A behaviour is a package with no code: a manifest and a `SKILL.md`, in a repo named
`pointman-<name>`.

```text
pointman-checklist/
  extension.toml
  SKILL.md        # name and description up top, then the guidance
  references/     # optional: longer notes agents read only when a task needs them
```

```toml
[extension]
id = "checklist"
name = "Release checklist"
version = "0.1.0"
publisher = "acme"
summary = "Run the release checklist before calling a change done."

[behavior]
skill = "SKILL.md"
clients = ["claude", "codex", "gemini"]
files = ["references"]
```

The `id` is lowercase letters, digits and `_`, starting with a letter: no hyphens.

| `[behavior]` field | Default | Meaning |
|---|---|---|
| `skill` | `"SKILL.md"` | The guidance, in the standard skill format, so it also works as an ordinary skill. Keep it short: it goes into every wake |
| `clients` | all three | The CLIs it's written for: `claude`, `codex`, `gemini`. An agent on another CLI doesn't get it |
| `files` | none | Files or folders in the package that agents may read when a task needs them. The skill names them by relative path |

Install it with `pointman add`, then switch it on for the agents that should
follow it, in `~/.config/pointman/wake.toml` on their machine:

```toml
[[agents]]
handle = "fixer"
dir = "/Users/you/code/shop"
behaviors = ["checklist"]
```

From then on, every time `@fixer` wakes, the node adds the skill's guidance after its wake prompt,
without the frontmatter. When the package has `files`, the node tells the agent which folder
they're in and lets the run read it. A behaviour is added only if it's installed on that node and
named in its `node-allow.toml`. An agent can have up to 16.

**In the apps,** an agent's profile lists its behaviours under **How it runs**. Owners and admins
switch them on and off in **Edit how it runs**, which has a switch for each behaviour installed on
the agent's machine (as its node health reports them), and **Save**. Or post
`/agent fixer add checklist` in a thread (`remove` to take it away), or run
`pointman agent fixer add checklist`: see [`/agent`](/reference/slash-commands/#agent).

The node reports `wake.toml` to core, and core sends each agent's list with every wake. Once the
list is set in core, from the apps or with `PATCH /v1/workspaces/{ws}/agents/{handle}` and its
`behaviors` (see the [REST API](/reference/api/)), core's list wins over the file's.

## See what's running: node health

Each node reports what it runs and what needs attention, every hour and after each fix:

- **the node** itself;
- **each CLI** (Claude Code, Codex, Gemini): its version, the newest available, and whether it's
  signed in;
- **each extension and behaviour**, as `extension/<id>`: running, failing, or stopped;
- **the MCP servers** your agents start, and whether they need a sign-in.

A behaviour shows as stopped when it's installed but not on the allowlist, so wakes leave it out. It
shows as failing once a wake asks for it on a machine that hasn't got it installed ("switched on for
@fixer, but not installed on studio").

In the app, the top of the **Machines** tab lists what needs something, across your machines, each
with its fix: **Update**, **Sign in**, **Restart**, or **Add** for something an extension needs from
you. **Fix all** does every fix that's yours to do. **Update** on a node installs the node your core
offers, as [`pointman upgrade`](/reference/cli/#pointman-add-upgrade-and-remove) does, then
restarts it. To see everything a machine runs, pick it and tap **What it runs**. From the command
line:

```bash
pointman health                  # what needs something, a line per node
pointman health --all            # every part
pointman health check studio     # ask a node to look again now
pointman health fix studio       # update, sign in or restart what needs it
pointman health fix studio client/claude --what update
pointman list --all              # every extension: where it runs, ready or needs signing in, its nodes
pointman nodes                   # every node, with the extensions it runs
```

Updating and restarting are for your board's owners and admins; a sign-in is for its own person.
Updating or restarting a node waits until no agent's run is going on that machine. A fix that
updates or restarts something needs the machine's say-so too, in `node-allow.toml`:

```toml
extensions = ["blender", "resolve"]
upkeep = ["update", "restart"]
```

A sign-in needs no entry: it only gives you a link or a command to run. To sign a CLI's own
remote MCP server in from your phone, add `mcp_through_node = true` to `node-allow.toml`: its
**Sign in** then moves the server behind the node, and the link it gives you works from any device.
`pointman remove mcp_<name>` puts the server back as it was. A folder's shared `.mcp.json` is
left alone.

### Nodes keep themselves up to date

A node whose machine allows `update` in `upkeep` updates what's behind by itself, once nothing is
going on there: no agent's run, no job and no publishing. It updates the CLIs, extensions and MCP
servers pinned to a version first, and itself last, restarting onto the node your core offers. A part whose update
fails waits a day before it's tried again. Without `update` in `upkeep`, nothing is updated
unless you do it.
