nuncius@0.4.0
CAUTION· startup onlyPublisher claims: "Enables Claude Code, Codex, and Cursor to delegate self-contained coding tasks to one another on the user's own subscriptions, with each task isolated in a git worktree and sandbox, usage tracked, budgets enforced, and results returned for review." Observed in sandbox: no network, touches nothing outside the project, writes no memory.
Coverage
Startup only; authenticated tool behavior not tested.- Tested
- server startup over stdio (initialize sent) · every file read, file written and host contacted by what ran
- Not tested
- the tool list (the server exited before answering) · what the tools do when called with real credentials
What this means for the label: Caution here covers what ran. The server's tools were not exercised with a real credential, so nothing about their behavior is asserted; the credential summary below says what to do with that.
Before you give it your token
- It will ask for
- no credential variables read in the code
- Where it talks to
- no vendor host named in the codeAlso apple.com — not the publisher's domain; check what for before connecting.
- What the honeypot saw
- no credential files read · no network
- What we could not test
- the tool list (the server exited before answering) · what the tools do when called with real credentials
Connect it under the guard. Startup only; authenticated tool behavior not tested. The code names no credential variable; the guard pins the network to what the honeypot saw and logs every call.
Never paste a credential into a chat, a ticket or a report, including ours. How the guard holds it.
Is [email protected] safe to install?
KillerSkills ran it in the KillerSkills honeypot sandbox (2 scenarios; startup only; authenticated tool behavior not tested) on 2026-11-14 and scored it 68/100 (Caution). It is not known to be malicious, but it has behaviour or code a security policy should decide on before install. Notable findings: Access to sensitive file paths; Access to sensitive file paths; Privileged sudo command. Summary: no network, touches nothing outside the project, writes no memory.
Behavior trace
Honeypot run · 20 s · 2 scenarios · 0 deviationsStatic findings
// so ignored files like .env stay closed), the keychain folder (the CLIs' logins), Codex's loginFix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.mkdir "$WORK/task" && chgrp _nuncius "$WORK/task" && chmod 2770 "$WORK/task"Fix: Request the narrowest permission that works; replace wildcards with the exact paths or hosts.// the target planted (e.g. `.cursor` -> ~/.ssh), or nothing under it is collected.Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.if sudo -n /sbin/pfctl -a com.apple/250.nuncius -s rules >/dev/null 2>&1 || true; then :; fiFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.'.ssh', '.aws', '.gnupg', '.config/gh', '.config/gcloud', '.azure', '.kube', '.docker', '.netrc', '.npmrc', '.pypirc',Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.if (r.status !== 0) return `cannot switch to task account ${a} without a password: the sudo rule from scripts/task-accounts/setup.sh is missing`;Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.// account, through the sudo rule setup installs.Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.# It stops the task accounts' processes, then removes the firewall rules, the sudo rule, theFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.# sudo ./scripts/task-accounts/setup.sh --no-firewall # the same without the pf firewall rulesFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.const all = { ...env, ...secrets };Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.visudo -cf "$tmp" >/dev/null || { rm -f "$tmp"; fail "the sudo rule did not validate"; }Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.# sudo ./scripts/task-accounts/undo.shFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.// Words in code or text, split finely enough that `os.system('nuncius setup')` yields nuncius, setup.Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.as() { sudo -n -u "$U" -- "$@"; }Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.// `nuncius` (`sudo -u nuncius codex ...` must not pass). Agents call `nuncius codex ...` unwrapped.Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.SUDOERS=/etc/sudoers.d/nunciusFix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.# sudo ./scripts/task-accounts/setup.sh # accounts, sudo rule, home pass-through, firewallFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.{ [ -n "$OWNER" ] && [ "$OWNER" != root ]; } || { echo "run it with sudo from your own account, not as root" >&2; exit 1; }Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.// --disable-auto-update (a hidden option, in the CLI since at least 2026.10.16): without it CursorFix: Pin versions and sources; vendor anything fetched at install time.[ -d /var/db/nuncius-homes ] && [ ! -L /var/db/nuncius-homes ] && rm -rf /var/db/nuncius-homes && say "removed the task accounts' home folders"Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.{ [ -n "$OWNER" ] && [ "$OWNER" != root ]; } || fail "run it with sudo from your own account, not as root"Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.sudo -n -u "$U" /usr/bin/pkill -KILL -u "$U"; sleep 1Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.SUDOERS=/etc/sudoers.d/nunciusFix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.sudo -n -u "$U" true 2>/dev/null || { echo "cannot switch to $U: run setup.sh first"; exit 1; }Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.say "removed the sudo rule"Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.const SECRET_PROBES = ['.ssh', '.aws', '.gnupg', '.config/gh', '.netrc', '.npmrc', '.zsh_history', '.bash_history', '.gitconfig', 'Library/Cookies'];Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.add('the accounts, the sudo rule and the home pass-through are in place', !problem, problem);Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.'env -i codex exec hi', 'env -u HOME codex exec hi', 'sudo -u bob codex exec hi', 'nice -n 10 codex exec hi',Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.chmod 0644 "$PLIST"Fix: Request the narrowest permission that works; replace wildcards with the exact paths or hosts.…ex login', 'cursor-agent status', 'grep codex README.md', 'echo cursor-agent', 'sudo codex --version', 'npx @openai/codex login', 'claude', 'claude mcp list', 'claud…Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.kill "$mine" 2>/dev/nullFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.chmod -R go+rX "$REPO"; chmod o+x "$WORK"Fix: Request the narrowest permission that works; replace wildcards with the exact paths or hosts.git -C "$REPO" worktree add -q "$WORK/task/wt" -b t 2>/dev/null; chgrp -R _nuncius "$WORK/task/wt"; chmod -R g+rwX "$WORK/task/wt"Fix: Request the narrowest permission that works; replace wildcards with the exact paths or hosts.kill "$lis" 2>/dev/nullFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.const sig = underProfile(profileFile, `kill -0 ${outsidePid} 2>/dev/null && echo SIGNALED || echo denied`);Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.spawnSync('node', [path.join(root, 'bin', 'nuncius.js'), ...args], { env: { ...env, ...extraEnv }, cwd: repo, encoding: 'utf8' });Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.spawnSync(process.execPath, [path.join(root, 'bin', 'nuncius.js'), ...args], { env: { ...env, ...extraEnv }, cwd, encoding: 'utf8' });Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.spawnSync('node', [path.join(root, 'bin', 'nuncius.js'), ...args], { env: { ...env, ...extraEnv }, cwd, encoding: 'utf8' });Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.["python3 \\ #<<EOF\nimport os; os.system('nuncius setup')\nEOF", 'nuncius settings'],Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.env: { ...env, CLAUDECODE: '1' }, cwd: repo, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'],Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.const lis = underProfile(profileFile, `/usr/bin/nc -l 0.0.0.0 ${port} 2>${sh(errFile)} & p=$!; sleep 1; if kill -0 $p 2>/dev/null; then kill $p; echo LISTENING; else cat ${sh(errFile)}; fi`);Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.chmod g+x "$WORK" && chgrp _nuncius "$WORK"Fix: Request the narrowest permission that works; replace wildcards with the exact paths or hosts.['sudo nuncius uninstall-hook', 'nuncius settings'],Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.const cli = spawnSync('node', [path.join(root, 'bin', 'nuncius.js'), 'codex', 'x'], { env: { ...env, NUNCIUS_DEPTH: '1' }, cwd: repo, encoding: 'utf8' });Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.chown root:wheel "$PLIST"Fix: Request the narrowest permission that works; replace wildcards with the exact paths or hosts.…>/dev/null 2>&1 && echo git-ok; cat "' + path.join(os.homedir(), '.codex', 'auth.json') + '" >/dev/null 2>&1 && echo LOGIN-READ || echo login-denied; } > who.txt\necho \'{"type":"turn.completed","usage":{"input_tokens":1,"output_tokens":1}…Fix: Remove the call, or declare the host in frontmatter and explain in the README what is sent and why.server = spawn('node', [path.join(root, 'bin', 'nuncius.js'), 'mcp'], { env: { ...env, ...extraEnv }, cwd: repo });Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.// Signals only within this sandbox: a target cannot kill nuncius's worker (which enforces itsFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.chmod -a "group:$GROUP allow search" "$OWNER_HOME"Fix: Request the narrowest permission that works; replace wildcards with the exact paths or hosts.chmod 700 "$d"Fix: Request the narrowest permission that works; replace wildcards with the exact paths or hosts.'timeout 1.5m codex exec hi', 'timeout inf codex exec hi', 'nice --adjustment 5 codex exec hi', 'xargs --max-args 1 codex exec', 'sudo -u nuncius codex exec hi', 'bash -c "nuncius codex hi"'])Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.chmod +a "group:$GROUP allow search" "$OWNER_HOME"Fix: Request the narrowest permission that works; replace wildcards with the exact paths or hosts.const cli = spawnSync('node', [path.join(root, 'bin', 'nuncius.js'), 'codex', 'x'], { env: { ...env, NUNCIUS_DEPTH: v }, cwd: repo, encoding: 'utf8' });Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.expect_denied "cannot signal your processes" "kill -0 $mine"Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.spawnSync('node', [path.join(root, 'bin', 'nuncius.js'), ...args], { env: { ...env, ...extraEnv }, cwd: repo, input, encoding: 'utf8' });Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.…haredArrayBuffer(4)), 0, 0, Number(process.env.FAKE_AFTER_LIMITS_MS)); } console.log(JSON.stringify({ type: 'assistant', message: { content: [{ type: 'tool_use', name: 'Write', input: { file_path: 'c.txt' } }, { type: 'tool_use', name: 'Ba…Fix: Remove the call, or declare the host in frontmatter and explain in the README what is sent and why.assert.equal((await call('get_task', { id: '../../etc/passwd' })).isError, true);Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.'sudo --user=bob codex exec hi', 'env --unset HOME codex exec hi', "env -S'codex exec hi'", 'env --split-string=codex exec',Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.['python3 -c "import os; os.system(\'nuncius config set x 1\')"', 'nuncius settings'],Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.put('notes.txt'); put('.zshrc'); put('.cache/hf/token'); put('repo/.env'); put('repo/.git/HEAD', 'ref: refs/heads/main\n');Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.execFileSync('git', ['-C', repo, 'config', '--worktree', 'remote.origin.url', 'https://user:[email protected]/r.git']);Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.for (const cmd of ['codex exec "fix it"', 'cd x && codex "do it"', 'FOO=1 cursor-agent -p "hi"', 'npx @openai/codex exec hi', '/opt/homebrew/bin/codex exec y',Fix: Pin versions and sources; vendor anything fetched at install time.spawnSync(process.execPath, [path.join(root, 'bin', 'nuncius.js'), ...args], { env: { ...env, ...extraEnv }, cwd, encoding: 'utf8' });Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.…fig.worktree" "$(git rev-parse --git-dir)/config.worktree"; echo end; } > gitout.txt 2>&1\necho \'{"type":"turn.completed","usage":{"input_tokens":1,"output_tokens":1}}\'\n', { mode: 0o755 }); writeConfig({ codexBin: p }); const r =…Fix: Remove the call, or declare the host in frontmatter and explain in the README what is sent and why.assert.equal(cleanLabel('rm \u200f-rf \u200bx\ufeff\u2060y'), 'rm -rf x y', 'zero-width and direction marks go too');Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.const plainEnv = { ...env, FAKE_CODEX_PRIMARY: '95' };Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.assert.equal(cleanLabel('Shell: rm\n-rf \x1b[31mx'), 'Shell: rm -rf [31mx');Fix: Replace encoded or assembled strings with plain literals and remove undeclared network calls.assert.equal((await get('/api/task/activity?id=../../etc/passwd')).status, 404);Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.….fsmonitor; cat "$(git rev-parse --git-common-dir)/config"; echo end; } > gitout.txt 2>&1\necho \'{"type":"turn.completed","usage":{"input_tokens":1,"output_tokens":1}}\'\n', { mode: 0o755 }); writeConfig({ codexBin: p }); const r =…Fix: Remove the call, or declare the host in frontmatter and explain in the README what is sent and why.writeConfig({ cursorBin: probe('kill -KILL $PPID; kill -TERM "$FAKE_PID"; true') });Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.'.ssh': '~/.ssh',Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.…ts `--version`: Nuncius relies on some undocumented CLI behaviour, so it warns (never refuses) when a CLI is newer or older than the versions Nuncius was verified with. Aft…Fix: Remove the text that addresses the agent rather than the reader; describe behavior, do not instruct.execFileSync('git', ['-C', repo, 'remote', 'add', 'origin', 'https://user:[email protected]/r.git']);Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.'.aws': '~/.aws',Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.put('notes.txt'); put('.zshrc'); put('.cache/hf/token'); put('repo/.env'); put('repo/.git/HEAD', 'ref: refs/heads/main\n');Fix: Remove the call, or declare the host in frontmatter and explain in the README what is sent and why.// perl's setsid leaves the process group, so the group kill misses it; it keeps stdout open.Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.…= app-server ] && exit 0\n{ git log --oneline -1; git status --short; } > gitout.txt 2>&1\necho \'{"type":"turn.completed","usage":{"input_tokens":1,"output_tokens":1}}\'\n', { mode: 0o755 }); writeConfig({ codexBin: p }); const r =…Fix: Remove the call, or declare the host in frontmatter and explain in the README what is sent and why.… status', 'grep codex README.md', 'echo cursor-agent', 'sudo codex --version', 'npx @openai/codex login', 'claude', 'claude mcp list', 'claude --version', 'nuncius claude "hi"']…Fix: Pin versions and sources; vendor anything fetched at install time.Cost to the agent
Tokens estimated at four characters each. The tool list is re-read by the agent on every turn, so it is the number to compare between servers that do the same job: ranked across the index.
Known vulnerabilities
Vulnerability advisories (OSV) cover npm and PyPI packages; this registry has no advisory database.
What would make this Verified
12 points to 80 · weights: behavior 45%, static 25%, prompt 15%, publisher 15%- OS command execution function call detected≈ +9 on the totalBuild commands from fixed arguments; do not interpolate untrusted strings into a shell.
- Unclaimed publisher≈ +8 on the totalClaim sdirkse: a verified publisher gets +50 on publisher trust. It is free and usually instant with GitHub sign-in.
- Access to sensitive file paths≈ +4 on the totalStop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.
- Privileged sudo command≈ +4 on the totalBuild commands from fixed arguments; do not interpolate untrusted strings into a shell.
- Auto-update or dynamic fetch configuration≈ +4 on the totalPin versions and sources; vendor anything fetched at install time.
- Destructive file system command detected≈ +4 on the totalBuild commands from fixed arguments; do not interpolate untrusted strings into a shell.