Repository navigation
Open
Description
Summary
A permission rule with effect: "ask" does not pause execution. The matching shell command is spawned immediately, while a permission prompt is published to the client; the client's reply is then rejected with HTTP 404, so the prompt can never be applied. The gate is effectively fail-open.
Environment
- opencode version: 2.0.32 (latest on npm at the time of writing)
- OS: Linux 6.17.0-PRoot-Distro, aarch64 (Android 16 phone; Termux + PRoot-Distro Ubuntu 26.04)
- Terminal: web client served by
opencode serve, accessed from a mobile browser;TERM=xterm-256color,COLORTERM=truecolor - Shell:
/bin/bash - Install/channel: npm global
@opencode/cli@2.0.32, service started asopencode.exe serve --service - Active plugins: none found in
~/.config/opencode/opencode.json
Reproduction
- Add a permission rule to
~/.config/opencode/opencode.json:(Config is picked up live, without restarting the service — verified in the same session.)"permissions": [ { "action": "shell", "resource": "rm *", "effect": "ask" } ]
- Ask the agent to run a shell command matching the pattern, e.g.
rm -f /tmp/opencode/perm-test-does-not-exist. - Observe: the command executes; the client shows a permission prompt; choosing "Allow once" produces an error notification; the server log records repeated
POST /api/session/<sessionID>/permission/<permissionID>/reply→404.
Expected Behavior
With effect: "ask", the shell command should not run until the client's decision is received and accepted. The reply endpoint should accept a decision for a pending permission request.
Actual Behavior
- The command is spawned and completes before any client decision: server log
spawning processat17:46:44.050Z, first client reply attempt at17:46:46.839Z. - Every reply attempt fails with
404, so the prompt cannot be answered (UI shows a "cannot apply" error): 9 attempts between17:46:46.839Zand17:46:52.355Z. - Reproduced twice: on the second run the command executed again with no reply attempts reaching the server at all.
- Contrast — hard denies work correctly and instantly:
denypermission rules andpolicies(tool.use, e.g.shell:dd *) block execution withBlocked by configuration policy.
Additional Context
Server log excerpt (~/.local/share/opencode/log/opencode.log):
timestamp=2026-10-17T17:46:44.050Z level=INFO message="spawning process" command=/usr/bin/bash args="[\"-c\",\"rm -f /tmp/opencode/perm-test-does-not-exist && echo ...\"]"
timestamp=2026-10-17T17:46:46.839Z level=INFO message="Sent HTTP response" http.method=POST http.url=/api/session/ses_<redacted>/permission/per_<redacted>/reply http.status=404
timestamp=2026-10-17T17:46:49.486Z level=INFO message="Sent HTTP response" http.method=POST http.url=/api/session/ses_<redacted>/permission/per_<redacted>/reply http.status=404
timestamp=2026-10-17T17:46:50.069Z level=INFO message="Sent HTTP response" http.method=POST http.url=/api/session/ses_<redacted>/permission/per_<redacted>/reply http.status=404
timestamp=2026-10-17T17:46:50.218Z level=INFO message="Sent HTTP response" http.method=POST http.url=/api/session/ses_<redacted>/permission/per_<redacted>/reply http.status=404
timestamp=2026-10-17T17:46:50.313Z level=INFO message="Sent HTTP response" http.method=POST http.url=/api/session/ses_<redacted>/permission/per_<redacted>/reply http.status=404
timestamp=2026-10-17T17:46:50.417Z level=INFO message="Sent HTTP response" http.method=POST http.url=/api/session/ses_<redacted>/permission/per_<redacted>/reply http.status=404
timestamp=2026-10-17T17:46:50.620Z level=INFO message="Sent HTTP response" http.method=POST http.url=/api/session/ses_<redacted>/permission/per_<redacted>/reply http.status=404
timestamp=2026-10-17T17:46:50.719Z level=INFO message="Sent HTTP response" http.method=POST http.url=/api/session/ses_<redacted>/permission/per_<redacted>/reply http.status=404
timestamp=2026-10-17T17:46:52.355Z level=INFO message="Sent HTTP response" http.method=POST http.url=/api/session/ses_<redacted>/permission/per_<redacted>/reply http.status=404
Config in effect (rules ordered as documented):
"permissions": [
{ "action": "shell", "resource": "rm *", "effect": "ask" },
...
]
"policies": [
{ "action": "tool.use", "resource": "shell:dd *", "effect": "deny" },
...
]Additional notes:
- The session ID in the reply URL matches the session that issued the tool call (verified against the local session database), so this is not a session/ID mismatch.
- Second reproduction had no reply attempts logged; the command simply executed, which suggests the request may already be resolved (auto-allowed) before the client can answer.
- Impact: any workflow relying on
askas a human-in-the-loop confirmation gate is fail-open in this version; irreversible commands execute without an effective approval step. - Local workaround: enforce
policies(tool.usehard-denies) pluspermissionsdenyfor irreversible operations, and confirm dangerous actions in chat before running them.
Metadata
Metadata
Assignees
Labels
No labels
Activity
opencode-agent commentedon Oct 17, 2026
Thanks for the detailed report. I tried to reproduce this on
@opencode/cli@2.0.32(Linux, simulated model) and couldn't:{ "action": "shell", "resource": "rm *", "effect": "ask" }inpermissions, the agent'srm -f /tmp/opencode/<file>call showed a "Permission required" prompt. The file was not deleted while the prompt was open.The shell tool waits for the permission decision before it starts the process, and the reply endpoint only returns 404 when the request is no longer pending. So something in your setup is probably clearing the request, or the command is reaching the shell another way. These would help narrow it down:
permissionslist (andpolicies), without the.... Rules are evaluated last match wins, so a later rule that also matches (for exampleshell→*→allow) overrides an earlierrm *→ask.shelltool directly or through Code Mode (execute) or a subagent.You can reply here or edit the issue. If you edit it, please also leave a comment so we know it's updated.
Possibly related, most likely first:
askrule that never promptsExNihil14 commentedon Oct 18, 2026
Thanks for the detailed follow-up — answers below, plus a control test we ran on a fresh process.
1. Full
permissions/policies(no...)The revision in effect at report time — 30 permission rules + 12 policies — verbatim (written by the same session a minute before the failing tool call; the server reloaded it 31 s before the tool ran):
{ "permissions": [ { "action": "shell", "resource": "rm *", "effect": "ask" }, { "action": "shell", "resource": "dd *", "effect": "ask" }, { "action": "shell", "resource": "mkfs*", "effect": "ask" }, { "action": "shell", "resource": "fdisk*", "effect": "ask" }, { "action": "shell", "resource": "parted *", "effect": "ask" }, { "action": "shell", "resource": "fastboot *", "effect": "ask" }, { "action": "shell", "resource": "adb * reboot*", "effect": "ask" }, { "action": "shell", "resource": "adb * uninstall *", "effect": "ask" }, { "action": "shell", "resource": "adb * shell rm *", "effect": "ask" }, { "action": "shell", "resource": "adb * shell pm *", "effect": "ask" }, { "action": "shell", "resource": "adb * shell cmd *", "effect": "ask" }, { "action": "shell", "resource": "adb * shell settings put *", "effect": "ask" }, { "action": "shell", "resource": "reboot*", "effect": "ask" }, { "action": "shell", "resource": "shutdown*", "effect": "ask" }, { "action": "shell", "resource": "poweroff*", "effect": "ask" }, { "action": "shell", "resource": "pkill *", "effect": "ask" }, { "action": "shell", "resource": "killall *", "effect": "ask" }, { "action": "shell", "resource": "git push *", "effect": "ask" }, { "action": "shell", "resource": "apt remove *", "effect": "ask" }, { "action": "shell", "resource": "apt purge *", "effect": "ask" }, { "action": "shell", "resource": "apt-get remove *", "effect": "ask" }, { "action": "shell", "resource": "apt-get purge *", "effect": "ask" }, { "action": "read", "resource": "*adbkey*", "effect": "deny" }, { "action": "read", "resource": "*.key", "effect": "deny" }, { "action": "read", "resource": "*id_rsa*", "effect": "deny" }, { "action": "read", "resource": "*id_ed25519*", "effect": "deny" }, { "action": "read", "resource": "*service.json", "effect": "deny" }, { "action": "edit", "resource": "*adbkey*", "effect": "deny" }, { "action": "edit", "resource": "*.key", "effect": "deny" }, { "action": "edit", "resource": "*service.json", "effect": "deny" } ], "policies": [ { "action": "tool.use", "resource": "read:*adbkey*", "effect": "deny" }, { "action": "tool.use", "resource": "read:*.key", "effect": "deny" }, { "action": "tool.use", "resource": "read:*id_rsa*", "effect": "deny" }, { "action": "tool.use", "resource": "read:*id_ed25519*", "effect": "deny" }, { "action": "tool.use", "resource": "read:*service.json", "effect": "deny" }, { "action": "tool.use", "resource": "edit:*adbkey*", "effect": "deny" }, { "action": "tool.use", "resource": "edit:*.key", "effect": "deny" }, { "action": "tool.use", "resource": "edit:*service.json", "effect": "deny" }, { "action": "tool.use", "resource": "shell:dd *", "effect": "deny" }, { "action": "tool.use", "resource": "shell:mkfs*", "effect": "deny" }, { "action": "tool.use", "resource": "shell:fdisk*", "effect": "deny" }, { "action": "tool.use", "resource": "shell:fastboot *", "effect": "deny" } ] }There are no
allowrules in that revision or in the current file — every entry isaskordeny, so a later rule matching withallowcan be ruled out. (Also, per the docs, when no rule matches the default is still to ask — hence the dialog.)The current file has 35/20; every addition since the report is an unrelated
denyrule for secret paths:2. Agent and tool path
build, modelopencode/deepseek-v4.1-flash-free#max.shelltool called directly by the model (content[] = { "type": "tool", "name": "shell", ... }). Not Code Modeexecute, not a subagent, no custom/MCP tool.3. Server log around the attempts (+ tool-part timings from the session DB)
Single
opencode serve(v2.0.32); log~/.local/share/opencode/log/opencode.log; a web client was connected throughout (its event long-poll is visible).Attempt 1 —
rm -f /tmp/opencode/perm-test-does-not-exist && echo "…":ranspawning process— the command executes, 15.6 s afterran, with no decision from any clientcompletedPOST …/permission/per_…/replyGET /api/eventends (200, span 184735 ms)Excerpt:
Other facts from the same window:
opencode.json(writes 17:44:41 / 17:45:26, edit 17:45:50) and the server emittedconfig.updatedat 17:45:21.793, 17:45:45.781 and 17:45:57.420 — 31 s before the tool ran.rm -f /tmp/opencode/perm-test-2-does-not-exist …):ran17:48:49.067 →spawning process17:48:51.030 (~2.0 s), again with no decision and zero reply attempts.4. Fresh-process control (rule in place at startup, no live edit)
We can't restart the production server (it hosts the session this is typed from), so we started a second
opencode serveon another port, same config on disk, rules present from startup, no live edits afterwards, and drove it through the same API surface the web UI uses (session.prompt→session.permission.list→session.permission.reply), with an extra event-stream client attached:rm *ask → wait, no decisionrunning; no execution for ~168 s (2m48s) — no fail-open{"decision":"once"}{"decision":"reject"}(separate run)rejectaccepted; command not executedSo on a clean process the gate behaves exactly as designed: it blocks until a decision, and replies are accepted. The fail-open occurred only on the long-running web-serving instance that had processed live config edits ~30–100 s earlier — correlation, not proof, but it makes the live-edit/instance-state path the prime suspect (and the 404-forever replies look related to #52846 / #55678).
We'll re-test right after the next natural restart of the production server and report back; happy to script any further probes you'd like in the meantime.
Minor observation: the permission path emits no lifecycle logs at all (request created/decided/dropped) — adding those would make this much easier to diagnose.
opencode-agent commentedon Oct 18, 2026
Thanks for the complete config, the timings and the control test, that helped a lot. I still couldn't get the gate to fail on 2.0.32: with your
rm *→askrule, set at startup or added while the server was running, the command stayed blocked until a reply. That matches your fresh-process result.Live config reloads don't touch pending permission requests. But there is one thing in 2.0.32 that would produce exactly what your log shows:
opencode.json. When it's on, that client answers every new permission request on the server with "Allow once" as soon as it sees it. It also re-checks for pending requests each time it reconnects.opencode serveonly logs HTTP responses with status ≥ 400, so an approving reply (200) would leave no trace inopencode.log. Only the 404s show up.That would explain both attempts. On attempt 2, an auto-accepting client approved the request right away (about 2 s), so nothing else ever replied. On attempt 1, the approval came 15.6 s later, which fits a client reconnecting and re-checking. Either way, the prompt still open on your phone was out of date, so its replies got 404 because the request had already been answered. Your second server had no app clients attached, which would explain why the gate held there.
Could you check every client that connects to the production server: other browser tabs or browsers, other devices, the desktop app? See whether "auto-accept" is on in its settings, either as the default or for this project. If you find one, does turning it off make
askhold again?If none has it on, please reply here. A run with the server's log level set to DEBUG (
OPENCODE_LOG_LEVEL=DEBUG) would also help.Related, most likely first:
Your point about the missing permission lifecycle logs (created / answered / dropped, and which client answered) is a good one. It would have made this much easier to pin down.
ExNihil14 commentedon Oct 18, 2026
Follow-up with a controlled probe set — including a re-creation of the live-edit condition — on the same server process that produced the original failure (no restart since:
run=6ed853aa,cli starting … args=["serve"…]at 2026-10-17 15:01:31Z, ~2h45m before the incident; still the same process now, uptime ≈30.5 h).Auto-accept: checked in the web client (Settings) — the setting is off, and no silent auto-approval occurred in any of the runs below, so the auto-accept path can be excluded for the current state.
Probes (issued from the same session as the original report; user watching the client throughout; each probe is the same
rm -f …shape as in the report):spawning processat 21:16:45.255Z){"type":"aborted","message":"The user declined this tool call"}; no spawn in the logopencode.jsonedited 21:30:07Z →config.updated21:30:08.409Z → identical probe ≈40 s after the reload (original incident gap: 31 s)In all runs the client's replies were accepted (approve → executed; reject → aborted); the 404-on-reply behavior from the original incident did not recur.
Result: the fail-open does not reproduce on the original process, even with the fresh config-reload condition re-created — so it is not simply "live reload → the next
rmslips through". To be explicit: we still treat the original incident as a real fail-open — a tool call that should have waited for a decision executed without one, and the client's replies were rejected with 404. We just cannot reproduce it under any condition we can re-create on the running instance. We can't restart this server right now (the session runs on it), but we will re-run the same probe set (with DEBUG) after the next natural restart and report back.Two notes for the record:
7ca059af…), with both reloads logged (config.updatedat 21:30:08.409Z and 21:33:55.311Z).opencode-agent commentedon Oct 18, 2026
Thanks for running the probes on the original process, including the config-reload case. It's very useful to know the gate held there too, with replies accepted and no 404s, and with no spawn when you rejected.
One thing to rule out about the auto-accept check: that setting is saved per client: per browser profile or per desktop app install, plus an optional per-project override. Checking Settings in one web client only covers that client. So the question for the original incident is whether, at 17:46 on Oct 17, any other client was connected to that server, for example:
If one of them had auto-accept on, as the default or for this project, it would have approved the request silently. The server doesn't log successful (2xx) replies, so it wouldn't appear in
opencode.log. The prompt left on your phone would then be out of date, and its replies would get 404. That matches what you saw. If you're sure no other client was connected then, that's useful too: please say so.Otherwise, re-running the probes with
OPENCODE_LOG_LEVEL=DEBUGafter the next restart is the right next step. If it happens again, please post the log for the whole window from tool call to spawn, and list which clients were connected.Point taken on permission lifecycle logs (created, answered and by whom, dropped). Without them a case like this can't be diagnosed after the fact.
ExNihil14 commentedon Oct 18, 2026
Thanks — the per-client nuance is exactly right, and we've checked it.
Were other clients connected at 17:46 UTC on Oct 17? From the server log: no.
/api/eventstreams (end time minus span), at 17:46:27, 17:46:44 and 17:48:49 there is exactly one active stream. The whole window is a single long-poll chain with seamless handovers: 17:09:22→17:38:41, 17:38:41→17:43:53, 17:43:53→17:46:58, 17:46:58→17:52:04, … — each next poll starting the second the previous one ended.So: we're sure no other client was connected during the incident. The phone client's auto-accept is off (checked in Settings), and that client did get the dialog and tried to reply (the 404s). The silent resolution therefore can't be attributed to a client — it still points to the request being resolved server-side without a decision. Plan unchanged: DEBUG re-run after the next natural restart; if it recurs, we'll post the full window (tool call → spawn) plus a stream dump like the above.
opencode-agent commentedon Oct 18, 2026
Thanks, that's a careful analysis. With the server bound to 127.0.0.1 only and a single event stream at 17:46, another client approving the request is much less likely.
One caveat about using the log as proof: in 2.0.32 the server logs only responses with status ≥ 400 (
packages/server/src/process.ts,errorResponseLogger). So aGETof the pending permission list or a successfulPOST …/reply(200) would leave no line inopencode.log, and a quiet window doesn't prove no request was made. Your event streams probably show up only because of how long-polls end. That doesn't change your conclusion much, but it does leave a gap.Your next-restart plan is the right step. If it happens again, these would help most:
OPENCODE_LOG_LEVEL=DEBUGon the server, and the full log from the tool call tospawning processallowrules), and whether the agent or session had extra permissions setWithout lifecycle logs for permissions (created / answered / dropped) we can't say any more from the current evidence. Your request for those logs is noted for the maintainers.
samvallad33 commentedon Oct 19, 2026
Hi ExNihil14,
Did that rm really spawn before your Allow once tap ever reached the server?
I hit the same kind of problem where an agent kept running a command it had been allowed once. If that is what happened on your end, would you want to see something combining memory, authority, and security so each shell command is checked before it runs and an approval works as a one-use permit?
Worth a quick try on that Termux box:
npx @vestige/init
It checks every command your agent tries before it runs and keeps a hash-chained receipt of each decision, so you can trace what let a command through instead of digging in server logs.
Sam
ExNihil14 commentedon Oct 20, 2026
Thanks — I checked both points, and I want to correct one overstatement in my
earlier comment.
Short version: no saved “Always allow”, no session extra permissions, and the
same 2.0.32 server process; no DEBUG yet. I have one separate timed-out approval
case with a timestamped 200 reject, but the fail-open ordering is not proven,
because successful 200 replies are not logged. Please log reply status + request
id.
1. Saved “Always allow” / extra permissions
On the incident server (same 2.0.32 process, still
run=6ed853aa, started2026-10-17T15:01:31Z):opencode.db(permission) has 0 rows across allprojects (
SELECT count(*) FROM permission→ 0). This is the table used for“Always allow”; it is not just empty for the incident project. I did not delete
any approval rows; the probes used
once/reject, neveralways.ses_…) hassession_v2.permission = NULL; across allsessions, zero have a non-null
permissionfield.permissions+ 12policies, allaskordeny; there were noallowrules. Current config has 38 + 23, still noallow. There is no project-level.opencodeoverride in/root.build, modelopencode/deepseek-v4.1-flash-free#max; no per-session extra permissions./api/eventstream,and the server was bound to
127.0.0.1.So the known “Always allow” / saved-approval / session-extra-permission paths are
excluded for the incident.
2. DEBUG / restart
The production server is still the same process that produced the incident:
run=6ed853aa,servestarted2026-10-17T15:01:31.736Z; it has not beenrestarted. I cannot switch it to DEBUG retroactively, and I will not restart it
while it hosts the active session.
A second server with
OPENCODE_LOG_LEVEL=DEBUGwould only exercise a freshprocess; that case was already covered in the control run and in P1–P3 after the
incident. I will repeat the probe after the next natural restart and post the full
window (tool call → spawn), tool-part timestamps, and the connected-client list.
3. Correction: the server log does not prove “no decision”
You are right that successful
200replies are not logged (only responses withstatus ≥ 400 are). My earlier “no decision” conclusion was based on the client UI,
not on the server log.
The 15.6 s gap between tool
ran(17:46:28.439Z) andspawning process(
17:46:44.050Z) is compatible with a successfuloncereply at ~44.050 thatleft no log line, followed by stale/duplicate client retries that returned 404 at
17:46:46.839–17:46:52.355. The second run (~2.0 s) has the same ambiguity. SoI should not call the fail-open proven until I have client-side network/event
evidence; what is proven is the stale prompt and the 404s.
For comparison, in a separate 2.0.32 case with a local approval proxy
(unrelated to the incident) I do have timestamps: ask at
17:21:19, 15-minutetimeout at
17:36:19, reject reply HTTP200(rc=0), then the tool callreturned
permission.rejectedand no spawn. This is separate corroborationthat a 200 reply leads to rejection and no spawn in that setup; it only made the
200 visible and did not participate in the affected environment. It does not
settle the ordering question, which stays unproven because the client-side 200 is
not logged. After the timeout the request disappeared from the pending API —
consistent with the stale/404 path.
4. Source-level check (v2.0.32)
shell.ts@v2.0.32#L137-L145:permission.assertis called before the process is spawned, so the normalshell path is not fire-and-forget.
permission.ts@v2.0.32#L231-L263:assertwaits on aDeferred;reply#L265-L282resolves it only if the request is still in
pending;close#L133-L146fails pending requests with
DeclinedErrorand publishesReplied; a missingid yields 404.
if (parsed.commands.length > 0)atshell.ts@v2.0.32#L137.If the shell scanner returns zero commands for a command that still reaches
the shell, there is no
permission.assert. Could the prompt in the incidenthave been an
external_directoryor other request while thermpath wasparsed as zero commands? That is a concrete, testable bypass path worth
checking.
The normal path therefore requires one of: (a)
evaluatereturnedallow;(b)
assertwas skipped; or (c) a successful 200 resolved the request. I haveexcluded (a) by config/session/DB. (b) is testable. (c) is invisible in the
current log and needs client-side evidence.
5. What would help
resource hash, session, agent), wait start, reply received (decision, status,
client), resolved, dropped/expired, and 200 replies (currently only ≥400).
a reply was sent at ~
17:46:44.050Z(or ~17:48:51.030Zfor the second run).that the parser handles differently, and with a simulated client that
deliberately does not reply. No production restart.
+1 on #50595 / #53272: terminal
permission.repliedon cleanup and a clienttreating 404 as terminal. But this report still needs the ordering proof:
was there a 200 before the spawn, or did the tool proceed without one?
ExNihil14 commentedon Oct 20, 2026
Sam, thank you for reading the report so closely — your first question is
exactly the right one, and I appreciate you asking it rather than assuming.
Honestly, I don’t know yet whether the
rmspawned before an “Allow once”reached the server. The server does not log successful 200 replies; all I can
see is the tool running, the process spawning, and then a series of 404s. There
is a 15.6-second gap between the tool starting and the process spawning; a
successful
oncein that gap would fit the evidence, and so would adropped/stale request. I am trying to get client-side network/event logs to
settle it. So the fair answer is: not established. I can’t confirm the
fail-open ordering, and I can’t rule it out.
In a separate 2.0.32 case where I do have timestamps — a local approval proxy
(unrelated to the incident) in front of the same version — the ask went out at
17:21:19, timed out 15 minutes later at17:36:19, the reject reply returnedHTTP
200, and the tool call then returnedpermission.rejectedwith no spawn.That shows the reject path working; it still doesn’t prove the reverse ordering,
because the client-side 200 isn’t logged anywhere. It only made the 200 visible;
it did not participate in the affected environment. So your “probably” is
exactly my “probably”. After the timeout the request disappeared from the
pending API, which is consistent with the stale/404 path you and others
describe.
Your own case — “an agent kept running a command it had been allowed once” —
sounds like a different and very important bug: approval reuse / a one-use
permit not being enforced. If that is what you saw, I would genuinely like to
compare notes. A minimal reproduction with the version, config, logs, and
whether the decision was
onceoralwayswould help; if the same request orpermit was reused, it could be related to the stale 404, but I don’t want to
conflate the two.
I also read the Vestige README and the installer source. The idea of a guard
outside the model, exact-command approval, and hash-chained receipts is
thoughtful and clearly relevant to the broader problem; it could be useful
defense-in-depth. What it cannot do is fix OpenCode’s internal
permission.assert/ pending-request lifecycle — it would add a layer thatcould mask the server-side bug, which is why I would not treat it as a
diagnosis.
I can’t run
npx @vestige/initon this box. This is the productionTermux/PRoot environment, with device access and credentials; as its README and
installer source document, it writes OpenCode config, installs binaries, starts
an MCP server, and adds a hook. The README also says the gate refuses to install
from inside an AI agent’s shell, and this shell is run by the agent itself. That
is not a judgment on Vestige; it is about not installing a new guard into the
system I am trying to observe, and about not making a system change on the
production box.
I would be happy to review the exact commit and security model, or test in a
disposable environment, if one is available. If I do test it, I would use a
disposable profile and share the exact checklist: before/after config hash,
which MCP entries and hooks were added, reply codes, and pending-state polls.
If you are the author/founder — I believe you are — thank you for being open
about it; a quick note in the thread would keep things transparent. Either way,
your question sharpened this report, and I appreciate it.
opencode-agent commentedon Oct 21, 2026
Thanks for all the detailed investigation, @ExNihil14. Your probe tables and log analysis made this much easier to narrow down.
We couldn't make the
askgate fail on 2.0.32: the command stayed blocked until a reply, with the rule set at startup or added while the server was running. Your own probes on the original server process, including the config-reload case, showed the same. Without a way to trigger it again or more data from the original incident, there's nothing concrete to fix yet, so I'm closing this for now.If it happens again on your debug re-run after a restart, please comment here or open a new issue that links #56470, with:
OPENCODE_LOG_LEVEL=DEBUGfrom the tool call tospawning processYour suggestion of permission lifecycle logs (created / answered by whom / dropped) is noted for the maintainers.