Features
Approvals and permissions
An agent that asks about everything is exhausting and an agent that asks about nothing is dangerous. Termdeck gives you four dials between those, and one set of refusals that do not depend on you being awake.
Setting this up for the first time? Approve Claude Code permissions remotely walks through it in order, start to finish.
The order every tool call goes through
When the engine wants to use a tool, four things happen on your own machine, in this order, before anything reaches your screen.
-
The call is scored for risk
From the tool, the command or path, and where that path sits relative to the folder the chat was opened in.
-
Hard denies are checked
A call that trips a guard is refused outright, with the reason attached. This runs first on purpose, so nothing further down can allow it.
-
Your auto allow rules are checked
If a rule covers this call and its risk is within the rule's cap, it runs. No prompt, no notification.
-
Otherwise it asks you
The call becomes a card in the transcript, an entry in the approval inbox, and, if you enabled it, a push notification. Nothing runs until you answer.
All four steps run on the machine, not in the browser. The browser is a management surface for the rules; the machine is what enforces them.
Steps two and three are Termdeck's own layer, and a single chat can opt out of both. See turning Termdeck's rules off for one chat below.
Permission modes
The mode is set per chat from the composer, and defaults come from Settings, then General. Termdeck uses one vocabulary for all three engines so switching engine does not rename every option under you. A chat keeps its mode across reloads and Termdeck updates in the browser you use it from.
| Mode | Behaviour | Available on |
|---|---|---|
| Plan only | Explores and proposes a plan. Changes nothing. | All three |
| Supervised | Asks before every command and every file change. | All three |
| Accept edits | File changes go through. Commands still ask. | Claude, Grok |
| Auto | Routine actions proceed. Risky ones still ask. | Codex |
| Full access | No prompts at all. The agent runs unattended. | All three |
Where an engine honours a mode differently, the interface prints the caveat under the picker rather than pretending the behaviour is identical:
- Codex, Supervised: Codex also restricts its sandbox to read only.
- Grok, Supervised: Grok refuses these outright rather than asking.
- Grok, Plan only: Grok has no plan mode of its own, so this behaves as read only.
- Claude, Plan only: Claude proposes a plan and waits for your approval before acting.
A mode an engine cannot honour is not offered at all, rather than shown as an option that quietly means something else.
You can change the mode while a turn is waiting on an approval. On Codex and Grok, switching to Full access answers the open card for you. On Grok, any switch decides the open card under the new mode, so moving to Supervised refuses a command that was waiting.
Full access is exactly what it says. It is the right mode for a scratch repository or a disposable VM and the wrong one for anything you would miss. The hard denies below still apply, but they are a backstop, not a substitute for supervision.
Turning Full access off for a machine
To make sure nothing on a machine ever runs without asking, add TERMDECK_FULL_ACCESS=off to that machine's ~/.termdeck/agent.env (on Windows, set TERMDECK_FULL_ACCESS=off in agent.env.cmd) and restart the agent. The machine then refuses Full access on Claude Code, Codex and Grok: a turn started in it fails with a message naming the setting. On Claude Code a switch to it during a turn is refused too; on Codex and Grok a switch during a turn still answers that turn's open cards, and the next turn is refused. The other modes are unchanged, and you still answer each approval yourself.
The approval card
An approval reads top to bottom. The header gives the tool, a risk badge, and how long the agent has been stopped waiting for you. Below it, in plain words, is what the call is about to do, then the exact command or path it will use, then a one line verdict on whether it looks safe.
The command is always shown in full, even when the agent supplied its own description of what it is for. The description is what the agent meant; the command is what will actually run, and it is the last thing you read before the buttons.
On Codex, the card shows the command as Codex will run it, without the shell wrapper around it, and Codex's own reason for asking, when it gave one, sits above it as the description.
On Codex, the card appears in the transcript, in place of the row for the command it is asking about, so the command is shown once, next to the buttons. Once you answer, the row comes back and shows the command running. If that row is not on screen, for example right after a reload, the card waits above the message box instead.
A card carries its risk level as a coloured stripe down its left edge and in the badge at the top. The card itself stays a normal card at every level, so a red stripe means something.
An approval does not wait forever. The foot of the card counts down from five minutes, and a request nobody answers in that time is denied, so the agent carries on with that answer instead of sitting blocked. Questions and plans have no countdown and wait as long as you need.
The buttons
They come in two groups, because only the first group answers the question on the card.
The answer to this one call. Deny refuses it and lets the agent carry on with that answer. On a low or medium card, Allow once is the highlighted choice. On a high or critical card the row flips: nothing is highlighted as an obvious yes and Deny is the marked one instead. On a critical card, Allow once also asks a second time before it goes through.
For this chat auto allows this tool for the rest of this chat and forgets it afterwards. On Codex it covers this exact command. Also on Codex, a simple command gets a third button named after its first two words and its flags, such as git clean -n this turn. Until the turn ends, it allows this command and any other in the same folder that starts with the same two words and has no flag this one does not have. Other arguments, such as another file or branch, are fine. A new flag is not: after git push, a git push --force still asks, and after git clean -n, a git clean -fdx still asks. Combined short flags count letter by letter, so -fdx is -f, -d and -x. A word starting with + or : also counts as a flag, because that is how git writes a force push and a branch delete. For a run command the next word counts too, so npm run build does not cover npm run deploy, and the same goes for commands like git stash, so git stash list does not cover git stash clear. A command with a pipe, a redirect, ;, && or $ never matches, and neither does one Termdeck rates above medium risk, or above the command you approved when that one was rated higher. The button is not offered for commands that run other commands (sudo, env, xargs, a shell, ssh, npm exec), for programs that delete, move, copy or download (rm, mv, cp, curl), for a command that starts with a flag, or on a critical card. Your saved rules and hard denies still apply to every command it covers. Always, on this machine saves a real rule that survives restarts and applies in your terminal too; you can remove it later under Settings, then Approvals. It asks twice, and it only appears when the engine offered a rule worth saving.
Allows this call and then puts the chat into Full access, so nothing else asks you for the rest of the run, risky commands included. It is the only button on the card that does not narrow to one tool. It asks twice: the first tap arms it and spells out what the second one does.
To come back out of Full access, pick any other mode from the composer. It is a mode like the others once it is on, not a one way door.
Plans and questions are separate cards, because they need a decision rather than a permission. Neither can ever be auto allowed: a question that answered itself would not be a question.
While a question is open the chat's status reads Waiting for your answer. Pick an answer and press Send answer, or press Don't answer to refuse it: the agent is told you declined and carries on without it. To answer later instead, close the dialog. The question stays open in the bar above the message box until you come back to it, or until its countdown runs out if the card shows one.
While a plan is open the chat's status reads Awaiting approval with how long it has waited, the same time the plan dialog shows. Approve and use auto mode is the filled, recommended choice; Approve and review edits asks you before each edit instead, Start with full access asks twice and then nothing asks you again, and Keep planning sends it back. On Codex the choices are Approve and implement, Implement with full access and Keep planning. To ask for a different plan, type what to change and press Send feedback.
Both stay readable in the chat afterwards. A plan reads as Proposed a plan with its title and your decision: Approved with the mode you picked (Approved, auto mode, Approved, review edits or Approved, full access; a plan you approved in the terminal says only Approved), Kept planning, Changes requested with You asked for changes: and your words under it, or Not answered when it was withdrawn first. An answered question reads as Asked 2 questions: Green, Medium please; open the row to see every question with your pick marked.
The approval inbox
The Approvals page collects every waiting request across every chat and every machine into one list. On a run that keeps asking the same kind of question, you can answer several at once instead of clicking through them one at a time.
How risk is scored
Every request lands in one of five bands. The score comes from what the call actually does, not from the tool name alone.
| Level | Typical causes |
|---|---|
| Interactive | A question or a plan. Always reaches you. |
| Low | Reads inside the project folder. |
| Medium | Running a shell command, changing git state, fetching from the network, a broad path scope, a shell pipeline. |
| High | Destructive git operations, permission or ownership changes, remote shell or sync, dependency installs, writing several files at once, reading outside the project, touching secret like files or project control files. |
| Critical | Writing outside the project, touching credentials, shell startup files or system paths, deploying or restarting services, deleting outside the project, and recursive deletes with a broad target. |
Because scoring is relative to the folder the chat was opened in, a narrow chat produces sharper answers than one opened at your home directory.
Auto allow rules
Rules are the answer to being asked the same thing forty times. Out of the box, three read only tools run without asking: Read, Glob, and Grep. Everything else asks.
A rule has four parts:
Which tool the rule applies to, for example Bash or Edit.
A pattern over the command for shell tools, or over the file path for file tools. For example git * or npm test. Leave it out and the rule covers any input for that tool, which is a much broader thing to do.
The rule only fires when the computed risk is at or below this cap. This is what stops git * from quietly covering git push --force to a shared branch.
Turn a rule off without losing it.
The rules list shows how many times each rule has fired and when it last did, so a rule that never matches anything is easy to spot and remove.
Presets
Common safe rules are one click, so you do not have to hand write a glob to allow reading git status. Hand written rules are there for the cases the presets do not cover.
What a rule can never do
- Auto allow a critical request. The cap tops out at high, and a value above that is clamped when saved.
- Auto allow a question or a plan. Those always reach you.
- Override a hard deny. Guards are checked first.
If your plan ends, saved rules are not deleted, only paused, and they come back the moment you subscribe again.
Hard denies
Some tool calls are refused before any of your settings are consulted. They exist because a red card is a thing people click Allow on at one in the morning, and because a read cannot mutate anything but can absolutely exfiltrate.
That last point is the concrete bug this closes. The default auto allow list contains Read, on the sound reasoning that reads are harmless. Reading ~/.ssh/id_ed25519 is a read.
| Guard | Refuses |
|---|---|
| Credential files | SSH keys, cloud credentials, .netrc, and GPG material, read or written, by a tool or by a shell command. |
| Remote scripts piped to a shell | curl | sh and its relatives: code fetched and executed in one step, which nobody reads first. |
| Privilege escalation | sudo from an agent you are driving over the internet. |
| Shell startup files | .bashrc, .zshrc, .profile and friends, where a write runs on every future shell. |
| System files | Writes to /etc, /usr, /var, /bin, and C:\Windows. |
| Deleting outside the project | An rm whose target is not inside the folder this chat was opened in. |
| Recursive deletes with a broad target | rm -rf against /, your home directory, or a wildcard. |
Each guard can be switched off individually in Settings, then Approvals, and only there. Switch one off when your work genuinely needs it, for example a chat whose whole job is provisioning a machine, and switch it back on afterwards.
Guards are keyed on stable identifiers rather than on risk level or on the wording of a reason, so improving a sentence can never silently stop something being refused.
Turning Termdeck's rules off for one chat
Open the options chip in the composer, the one that reads model, thinking and permission. Under Permissions is Termdeck rules, set to On or Off for that chat alone.
While it is off, the chip itself reads rules off next to the permission mode, so the chat says what it is running under without you opening anything.
Off means nothing you saved on the Approvals page is consulted here: no always allow tools, no saved rules, and no hard denies. Every tool call the permission mode leaves open becomes a card for you to answer.
It is not a way to make an agent run unsupervised, and it cannot be. Ask is the strictest answer this layer has, so switching it off can only ever produce more prompts, never fewer. If you want fewer prompts, that is the permission mode above it, or a rule.
Use it when Termdeck's own layer is in the way of what the chat is actually for: a chat that provisions a machine and needs sudo, one that manages credentials on purpose, or one where a rule you wrote is auto approving something you now want to see. The alternative, switching a guard off in Settings, turns it off for every chat on the account.
What to know about the switch itself:
- It is per chat, and it is remembered for that chat in the browser you set it in.
- It applies to the turn already running, not only to the next one.
- Every message you send carries it, so the device you send from decides how that turn is policed. Send from a second device that shows the switch On, and that turn is policed.
- It is never sticky by accident. Nothing writes it to disk, so if the service restarts, the chat is policed again.
- Your permission mode is unaffected. Plan only still plans, and Full access still runs unattended.
- Stop asking on a card still works. For this chat is a grant this chat keeps, and Always, on this machine writes a rule into the engine's own settings, which this switch does not touch. It is Termdeck's own saved rules that stop applying.
Interaction with your own CLI hooks
Termdeck's rules sit alongside whatever your coding CLI is already configured to do. A PreToolUse hook in your CLI settings can block a call before Termdeck ever sees it, which looks from the browser like a tool call that simply did not happen. Run checks on the machine card lists any armed hooks for exactly this reason.
Similarly, a CLI configured to bypass its own permission prompts will not raise them, so there is nothing for Termdeck to forward. Both facts are visible in the diagnostics rather than left to be discovered.
A Claude PermissionRequest hook can answer a request while its card is already on your screen. When it does, the card closes by itself and the chat carries on with the hook's answer. If a card vanishes before you touch it, look in your Claude settings for a hook that allows or denies calls.
On a Claude chat, the permissions control says when this is the case. If the machine's Claude settings have a PermissionRequest hook, or an allow rule that covers a whole tool such as Bash or Edit(*), the mode reads + settings in amber, and the options card names the hook and the tools. Those requests can be answered on the machine before they reach you, so the mode you pick covers only what they leave open. Narrow rules such as Bash(npm test:*) are not flagged.
When a PermissionRequest hook answers a request, the tool call's row in the chat says allowed by hook or denied by hook, and it still says so after a reload.
Spend ceilings
A per turn maximum makes the engine stop itself when the turn's cost reaches the limit. It is set by you for your own safety rather than by your plan, since the tokens bill to your provider account. Machine operators can impose a hard ceiling with TERMDECK_MAX_BUDGET_USD, and the smaller of the two wins.
A setup that works
- Start every chat in Supervised and watch what it asks for.
- After a day, look at what you approved repeatedly and turn those into rules with a tight glob and a low or medium cap.
- Move day to day work to Accept edits or Auto. File changes are reviewable in the diff and revertible with git; commands are not.
- Keep the hard denies on. They cost you nothing on a normal day.
- Reserve Full access for a repository you would be happy to delete.