Vectrum
Sign In
70 lines · 3.7 KB

Safety

Three layers, in increasing order of how much they actually protect you.

Path jail. Every path a tool is given is resolved with filepath.EvalSymlinks and refused if it lands outside the working root, including through a symlink that points out of the tree. Cheap, and it catches a lot.

Approval prompts. Read-only tools run unattended. bash, edit_file, write_file, web_search and web_fetch ask, with "yes / no / always this exact one / always this tool" and session memory. /allowed lists what you have agreed to. --allowedTools and --disallowedTools write the same decisions down ahead of time, in Claude Code's rule syntax — see using it in place of claude — and a deny rule there is checked before everything else, -yolo included.

Shell commands are parsed with mvdan.cc/sh/syntax so the prompt can tell you what a command will actually run — git status; rm -rf ~ is shown as running both git and rm. This is not a security boundary, and Codey does not pretend otherwise: a command containing $(...), backticks, or an interpreter with -c cannot be analysed at all, so it is marked opaque and "always allow this exact command" is refused for it. String-matching allowlists for shell commands do not work; this exists to make the prompt specific, not to make the decision for you.

Claude Code's permission rules sit in front of all of that. Its deny rules hold in every mode, -yolo included, which makes them the one part of this layer you can lean on: a call they refuse does not run. Its allow rules are matched with the same parser, one simple command at a time, and an opaque command matches none of them — but the warning above applies to them with full force. Bash(find:*) allows find . -exec rm {} +, and no parser will tell you otherwise.

Hooks sit outside all three layers, deliberately: a command in "hooks" runs on every event it is attached to, without a prompt, because you put it in your own config rather than the agent asking for it. The path jail is what keeps the agent from adding one — the config lives in ~/.config/codey/, outside any working root you would sensibly point Codey at — but the flip side is that anything able to write that file has arranged to run commands here. -no-hooks is the switch that says no for one run. MCP servers are the same bargain: each is a program started with your environment when the session opens, and only its tool calls go through the prompts. -no-mcp is theirs. The one kind of server that does not come from a file you wrote — a repository's .mcp.json — waits for you to trust it first.

A sandbox. This is the layer that actually contains a mistake, and the Codey cannot provide it for you. The honest recipe on Linux:

podman run --rm -it \
  -v "$PWD":/work:Z -w /work \
  -e ANTHROPIC_API_KEY \
  docker.io/library/golang:1.26 \
  /work/codey -C /work -yolo

Decide about egress explicitly. The container above has network access, because Codey needs to reach the API — which means the agent's bash has it too. That is the line between "broke my repo" and "read my .env and made an HTTP request". If that matters for the tree you are pointing it at:

  • Treat every secret reachable from inside the container as exposed.
  • Use a token scoped to nothing else, and a clone rather than your working tree.
  • For real isolation, run Codey on the host and put only bash in a network-less namespace. That is not implemented here.

-yolo skips every prompt. It is correct inside a container or against a throwaway clone, and wrong everywhere else. The eval runner sets it, because it is headless and operates on a temp directory it created.