SECURITY NOTE · MULTI-AGENT SETUPS

Skill directories are an instruction boundary

A per-agent skill allowlist is a control only when the skill source is per-agent. If every agent runs as one OS user and loads a shared, writable skill directory, that directory is a cross-agent instruction channel: an entry written by one agent loads into every other agent's context on its next run — including the most privileged agent's.

A skill is an instruction payload, not a label. The same is true of plugins. This note covers the class of weakness and one check you can run today; it names no product and no vendor.

Why the directory is the boundary

A skill is text a runtime feeds to the model. It is not a capability token that can be scoped by name: whatever the file says, the agent reads it.

An allowlist that filters by name assumes the runtime resolves those names from a source you control. When the runtime loads a shared directory, the directory decides which names exist. If that directory is writable by the agent's OS user, every agent running as that user can add, replace or remove entries — and every other agent loads them on its next run.

No exploit is required. The write is ordinary file access that every agent already has. File modes do not separate agents that share one uid: chmod 755 on the directory and chmod 444 on the files are advice the same user can overrule. Under one identity, ownership and permissions are not a boundary.

Check the source, then the loaded list

Do not audit the allowlist text first. Two checks tell you whether the control is real:

  1. The source. For each agent, list every skill directory its runtime loads. Compare the owner and mode of each directory with the uid the agent runs as. If the agent's uid can write the directory, treat that directory as an input channel.
  2. The result. Ask the runtime what it actually loaded, and compare that list with the configured allowlist. A name that loaded and was never configured is the symptom.

Run this as each agent's OS user, passing every skill directory that user's runtime loads:

#!/bin/sh
# skills-audit.sh — run as each agent's OS user; pass every skill
# directory that user's runtime loads.
echo "user: $(id -un) (uid $(id -u))"
for dir in "$@"; do
  if [ ! -d "$dir" ]; then
    echo "missing  $dir"
    continue
  fi
  ls -ld "$dir"          # owner, group, mode
  if [ -w "$dir" ]; then
    echo "  writable by this agent: entries can be added or replaced"
  fi
done

Then capture the configured list and the loaded list, one skill name per line, and diff them:

sort -o configured.txt configured.txt
sort -o loaded.txt loaded.txt
comm -3 configured.txt loaded.txt

Lines in the second column are loaded but not configured. That column is the one to explain. If the runtime prints its available skills at session start, capture that output as the loaded list before the agent does any work.

What we measured

We measured this on a multi-agent setup. The arrangement described above is not a theory: a shared skill directory writable by every agent's OS user is a cross-agent instruction channel, and an entry one agent places there is loaded by the others on their next run.

The fixes

  1. Build a per-run skill directory. For each run, create a fresh directory containing only the entries on that agent's allowlist, point the runtime at it, and delete it when the run ends. The shared directory stops being a source. This is the immediate control, and it is only as good as the process that builds it: if the runtime still loads another directory, that directory is part of the source too.
  2. Use a separate OS identity per agent. One uid per agent, each with its own home and skill directory, makes ownership and modes bind. Under a shared uid, none of the other fixes can enforce anything against a compromised agent.
  3. Keep shared directories read-only to agents. A shared skill library should be owned by a separate identity and not writable by any agent uid. If agents must publish entries, make that a reviewed step outside the run.

What this does not show

A filtered directory is a control on the source, not an identity boundary. An agent that can write another agent's files can still reach its skills. Only separate OS identities change that. This note describes a class of weakness in a common arrangement; it is not a disclosure about a named product. The check above is the part to keep: compare the source, then compare what actually loaded.