Repository navigation
Description
Summary
The permission-glob matcher in opencode 1.14.30 doesn't match patterns of the form **/<segment>/<segment>/<dir>/** against absolute resolved paths like /private/tmp/<segment>/<segment>/<dir>/<file>. Only single-segment patterns like **/<basename>/** match reliably. Patterns starting with a literal / (e.g. /tmp/scope/**) also don't match.
This makes it impossible to express "allow exactly this absolute path subtree" precisely — you have to fall back to **/<basename>/** which over-matches if the basename appears elsewhere in the filesystem.
Environment
- opencode 1.14.30 (curl install, macOS 25.3.0)
- Permission engine in
OPENCODE_CONFIG_CONTENTJSON
Reproduce
mkdir -p /tmp/agent-out
rm -f /tmp/agent-out/x.txt
CFG_FAILS='{"permission":{"edit":{"*":"deny","**/private/tmp/agent-out/**":"allow"},"write":{"*":"deny","**/private/tmp/agent-out/**":"allow"},"read":"deny","bash":"deny","external_directory":"allow"},"tools":{"bash":false,"edit":true,"write":true,"web_fetch":true,"web_search":true}}'
echo "Use the write tool to create /tmp/agent-out/x.txt with content X. Reply DONE." | \
OPENCODE_CONFIG_CONTENT="$CFG_FAILS" opencode run --model <any-model> --dangerously-skip-permissions
ls /tmp/agent-out/x.txt # ❌ does not existSame setup but with **/agent-out/** instead of **/private/tmp/agent-out/**:
CFG_WORKS='{"permission":{"edit":{"*":"deny","**/agent-out/**":"allow"},"write":{"*":"deny","**/agent-out/**":"allow"},...}}'
# ✅ writes the fileTested patterns (all targeting /private/tmp/agent-out/x.txt):
| Pattern | Result |
|---|---|
/private/tmp/agent-out/** |
❌ blocked |
private/tmp/agent-out/** |
❌ blocked |
**/private/tmp/agent-out/** |
❌ blocked |
**/tmp/agent-out/** |
❌ blocked (mostly; sometimes flaky) |
**/agent-out/** |
✅ matches reliably |
**/agent-out/* |
✅ matches reliably |
The agent-side error message confirms the rule IS in opencode's evaluated rules list, just doesn't match:
"relevant rules [{...},{"permission":"edit","pattern":"*","action":"deny"},
{"permission":"edit","pattern":"**/private/tmp/agent-out/**","action":"allow"}]"
Expected
Per the docs (https://opencode.ai/docs/permissions/), ** should match any number of path segments and last-match-wins. So **/private/tmp/agent-out/** should match /private/tmp/agent-out/x.txt and the allow should override the catch-all deny.
Suggested investigation
- The matcher likely doesn't anchor
**correctly when the path starts with/. Maybe it's stripping the leading/from the input path but then the pattern's leading**/requires non-empty segments, andprivate/tmp/...has zero segments beforeprivatefrom the matcher's POV. - Or the matcher treats multiple
**differently from one —**/dir/**works,**/seg/dir/**doesn't. - Test against Bun's built-in
Glob.matchif that's what's being used; or wherever the rule resolution happens insrc/permission/.
Workaround
Use **/<basename>/** in the config and enforce the actual path scope before spawning opencode (we validate against an ALLOWED_ROOTS list at the spawn boundary, then trust opencode for defense-in-depth).
Impact
For folks building MCP wrappers / sandboxes around opencode (e.g. our sub-agent-mcp), this means opencode's permission engine can't be the primary security gate for path scoping — you have to validate paths in your own code first. That's a reasonable defense-in-depth posture but probably not what the docs imply.
Activity
github-actions commentedon Apr 30, 2026
This issue might be a duplicate of existing issues. Please check:
editpermission patterns with absolute paths (e.g./tmp/foo/**) fail to match because the path passed to the matcher is relative toInstance.worktree, not absolute. That issue also identifies thatexternal_directoryuses absolute paths whileedituses relative paths, producing the same mismatch you're seeing.If your issue is specifically about the
**/seg1/seg2/dir/**multi-segment globstar not matching even relative paths, that would be a distinct bug worth keeping open separately.bmeindl commentedon May 1, 2026
Re-ran the original repro today on opencode 1.14.30 (same version as in the OP) on macOS 26.3 — and the multi-segment globstar patterns I had reported as failing now match correctly. Only the leading-slash absolute patterns still fail, which lines up exactly with #20045's diagnosis (
editreceives paths relative toInstance.worktree, so a pattern starting with/can't match the un-slashed input).Fresh table, target =
/tmp/agent-out/x.txt, sameOPENCODE_CONFIG_CONTENTshape as the OP, only the pattern varies:**/agent-out/****/tmp/agent-out/****/private/tmp/agent-out/**/tmp/agent-out/**/private/tmp/agent-out/**So my original report of a distinct multi-segment-globstar bug doesn't reproduce anymore. The remaining failures are the same root cause as #20045 (and the broader unification ask in #22465).
Happy for this to be closed as duplicate of #20045 — feel free to close, or leave it linked from there if useful as another data point. Thanks for the bot's triage hint, that pointed me at the right cluster.
github-actions commentedon Jul 14, 2026
To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.