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.
What an extension is
Section titled “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.
Every table and field of the manifest is in the extension.toml reference. To make one, see Write an extension.
Ask a machine to do something
Section titled “Ask a machine to do something”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.
Add an extension to a machine
Section titled “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:
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.
pointman add checklist # from your workspace's cataloguepointman add 'checklist>=0.2,<1' # within a range of versions, as uv writes thempointman add acme/pointman-checklist # a repo, brought into the catalogue (owners)pointman add checklist --node linux-box # on another machine's nodepointman add ~/code/pointman-checklist # a folder, while you're writing oneYour 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.
Give an extension what it needs
Section titled “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 addin any thread’s reply box (with the extension’s id in place oftickets). 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 |
Links as pills
Section titled “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:
[[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
Section titled “Behaviours”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.
See what’s running: node health
Section titled “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 node update does, then
restarts it. To see everything a machine runs, pick it and tap What it runs. From the command
line:
pointman health # what needs something, a line per nodepointman health --all # every partpointman health check studio # ask a node to look again nowpointman health fix studio # update, sign in or restart what needs itpointman health fix studio client/claude --what updatepointman list --all # every extension: where it runs, ready or needs signing in, its nodespointman nodes # every node, with the extensions it runsUpdating 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.
Nodes keep themselves up to date
Section titled “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.