Skip to content

Extensions

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.

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.

Every table and field of the manifest is in the extension.toml reference. To make one, see Write an extension.

Agents use two tools for every command extension:

{
"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.

The machine always has the last word. Its node installs only extensions named in its own allowlist, ~/.config/pointman/node-allow.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:

extensions = ["blender"]

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

Terminal window
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, pointman list shows what’s installed, and pointman remove <id> stops one and takes it off the allowlist. See the command line reference.

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, and the extension gets it through the vault.

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

[[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

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:

[[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.

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

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
[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:

[[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.

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), core’s list wins over the file’s.

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 node update does, then restarts it. To see everything a machine runs, pick it and tap What it runs. From the command line:

Terminal window
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:

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.

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.