Skip to content

Permissions

reyn's permission system gates access to file paths, shell, MCP tools, named tools, and Python preprocessor steps. Defaults are conservative; anything outside the defaults requires either a skill-level declaration plus user approval, OR a project-wide pre-approval in reyn.yaml.

Default grants (no declaration needed)

Op Scope
file.read / file.glob / file.grep Any path under the project root (CWD).
file.write / file.edit / file.delete Only under <CWD>/.reyn/ or <CWD>/reyn/.

Anything outside these defaults must be declared.

Skill declarations (permissions: in skill.md frontmatter)

Phase-level permissions: was removed. All permission declarations belong in skill.md frontmatter — see skill-md.md. Phases inherit whatever the skill declares.

---
type: skill
name: example
entry: main
final_output: result
permissions:
  shell: true
  mcp: [my_server]
  tool: [web_search]
  file:
    read:
      - path: ~/notes
        scope: recursive
    write:
      - path: /tmp/output
        scope: just_path
  python:
    - module: stats
      function: compute
      mode: safe
      timeout: 30
---

shell

true to enable the shell Control IR op for this phase. Off by default.

Even with shell: true, the runtime requires --allow-shell at startup; otherwise the op emits shell_not_allowed.

mcp, tool

List of MCP server names / named tool ids the phase may call.

file.read / file.write

For paths outside the default zones. Each entry has:

  • path — absolute, or relative to CWD. ~ is expanded.
  • scopejust_path (this exact path) or recursive (this path and everything below it).

file.write covers write, edit, and delete ops.

python

Per-(module, function) declarations for python preprocessor steps. See reference/dsl/preprocessor.md.

  • module, function — must match the corresponding preprocessor step.
  • modesafe (sandboxed) or unsafe (no AST sandbox; needs --allow-unsafe-python at runtime).
  • timeout — wall-clock seconds before the parent SIGKILLs the child. Default 30.

Web ops (Tier 1 — default allow)

web_search and web_fetch are Tier 1: they pass through by default without any declaration. No permissions: entry is needed to use them (FP-0022).

They can be restricted project-wide in reyn.yaml:

permissions:
  web.search: deny   # block all web_search ops
  web.fetch: deny    # block all web_fetch ops
  web.fetch: allow   # explicit pre-approval (skips per-run prompt entirely)

This differs from Tier 2-3 ops (shell, mcp) which require an explicit declaration in skill.md before the op is even attempted.

Approval flow (interactive)

When a phase declares a non-default permission, reyn shows a single startup prompt:

[approval] my_skill/file.write needs:
  /tmp/output (just_path)

  [y] allow this run only
  [j] persist approval for this exact path + skill
  [r] persist approval for the parent dir (recursive) + skill
  [N] deny

Persistent choices land in .reyn/approvals.yaml keyed by <skill>/<op>/<path> (with a trailing / for recursive grants). External skills cannot reuse another skill's approvals — keys are skill-scoped to prevent privilege escalation.

Project-wide pre-approval (reyn.yaml)

permissions:
  shell: allow
  file.write: allow         # grants ALL write-class ops for ALL skills
  python:
    safe: allow             # auto-approve all safe-mode python steps
    unsafe: allow           # also requires --allow-unsafe-python at runtime
    allowed_modules:
      - math
      - statistics
      - mypackage

Use allow only when the project is trusted. ask (the default) prompts; deny rejects.

Non-interactive runs (CI, eval)

reyn eval runs non-interactively — there is no prompt. Approvals must be pre-arranged either in reyn.yaml or .reyn/approvals.yaml (e.g. by running the target skill once interactively first).

Inspecting and revoking

reyn permissions list             # show saved approvals
reyn permissions revoke <key>     # remove an approval

See also