Reference
Security and privacy
You are installing a program that can drive a coding agent on your machine. That deserves a straight account of what it can do, what it cannot, and what you are trusting.
Termdeck is operated by Termdeck LLC. This page describes how Termdeck LLC handles your data and protects access to your machines.
The design in one sentence
The agent is a narrow set of typed capabilities rather than a remote shell, so the worst a compromised Termdeck could do to your machine is bounded by that list rather than by your file permissions.
What the agent will do
This is the complete surface. Everything Termdeck does is composed from these.
| Capability | Bounded by |
|---|---|
| Read, stat, list, and watch files | The transcript directories of the installed engines, and nothing above them. |
| Browse and read project files | The folder the chat was opened in, read only, enforced on both ends. |
| List folders for the folder picker | Folder names only, one level at a time. No file names and no contents. |
| Start a process | The three known engines only. There is no capability that runs a command of the caller's choosing. |
| Install a coding CLI | Runs npm install -g for one of the three engines' official packages. Termdeck names the engine; the package name is fixed in the agent, so no other package can be installed this way. |
| Write to a process, read it, stop it | Processes it started itself, and their children. The one exception is Take over: it may stop a Claude Code terminal session that registered itself on your machine. Any other process is refused, whatever Termdeck asks. |
| Report machine facts | Installed CLIs, login state, rate limit percentages, agent log. |
| Rename, tag, duplicate, or remove a chat, and save its settings | Files inside the transcript directories. A removed chat is moved to a trash folder in the same directory, not deleted. |
| Restore a checkpoint | Files that the engine itself snapshotted for that session. |
| Write the project instruction file | A fixed two entry filename table, resolved from a real session's own record. The caller names a session and an engine, never a path. |
| Save a file you attach | ~/.termdeck/uploads, one folder deep. The agent computes the folder itself and is only told a name and an allowed file type. Nothing is written executable, nothing overwrites anything that already exists, and nothing lands inside your project. |
| Switch saved account | Moving a credential file the CLI itself wrote. |
| Create a folder | One empty folder, named by you in the folder picker, directly inside a folder that already exists. It never overwrites and never writes a file. |
| Open a pull request | Runs gh pr create in the chat's own checkout, with your title and body and your own GitHub login. |
A request that is not on this list has no handler. There is no escape hatch, no generic exec, and no path parameter that reaches outside its confinement.
What the agent will not do
- Run a command on its own. There is no capability for it. Commands run only inside a coding agent's turn, under that chat's permission mode, and a machine with
TERMDECK_FULL_ACCESS=offrefuses the mode that runs them without asking. - Read an arbitrary file. Reads are confined to transcript roots and the open chat's project folder.
- Write an arbitrary file. The only writes are the narrow ones in the table above: chat housekeeping inside the transcript directories, checkpoint restore, the project instruction file, an attachment, a saved account switch, and one empty folder. An attachment and the instruction file you edit are the only ones whose bytes come from the browser.
- Accept an inbound connection. It dials out and listens for nothing.
- Send your source code anywhere. Repository content crosses the wire only when you open a file or a diff in the dock, and then only to your own browser.
What crosses the wire
| Crosses | Does not cross |
|---|---|
| Transcript content for chats you open, streamed on demand | Your provider passwords, API keys, or subscription tokens |
| Live turn output while you are watching | Repository content you have not opened |
| File contents and diffs you explicitly open in the dock | MCP auth headers, environment values, and credentials in URLs |
| Rate limit percentages | The tokens those percentages were read with |
| Token counts for the usage page | The transcripts those counts were derived from |
Rate limits are read on your own machine against the provider's endpoint, and only the resulting percentage is sent. Usage totals are computed on your machine as well; the scan does not upload the transcripts it scans.
What the master stores
- Your account: email, authentication, subscription, and plan grants.
- Your machine list, with each machine's token stored as a hash rather than the token itself.
- Push subscriptions for the devices you enabled notifications on.
- For each browser signed in to your account: when it signed in and was last active, its browser's user agent, and the IP address and country it connected from. You see these under Active sessions, with the address shortened to its first part. The record is deleted when that browser logs out or its sign-in expires, 30 days after it began.
- Per chat preferences you set here, such as a custom title, pinned state, or archived state.
- The chat list itself: each chat's title, its project folder and when it last moved, kept so the sidebar is there right after a Termdeck update instead of being read from every machine again. A title comes from a chat's first prompt or from a rename, so a few words of a prompt can sit in this list. The rest of the transcript never does.
- Aggregate operational counters, for example turns per day, used for billing and for the admin console.
- A record of how far your account has got: the first and latest time it reached steps such as seeing the plans page, connecting a machine, opening a chat or running a turn, and the reason you picked if you cancelled. It names steps and which machine; it never holds what you typed.
It does not store transcripts, source code, provider credentials, or diffs. Nothing is persisted from a chat you open beyond what is listed above.
Your browser keeps its own copies to load faster: recent transcripts, your preferences, and the words you typed but did not send. Clear local data under Settings, Data controls removes the transcript copies and preferences, including each chat's remembered permission mode. It keeps unsent drafts and each chat's prompt history, because those are your own work. On a shared computer, clear the site's data in the browser's own settings to remove those as well.
Your sign-ins
Settings, Security & login, Active sessions lists every browser signed in to your account: the browser and system, an approximate location (a country and the first part of the address), when it signed in, and when it was last active. The browser you are reading it in is marked Current session.
Log out on a row ends that browser's sign-in. Its next request is refused, and the push notifications it subscribed to stop, so a lost phone no longer receives Allow and Deny buttons. Log out of all other sessions does the same to every browser except the one you are using. Neither touches your machines: an agent signs in with its own machine token, not your password or a browser session, so to cut a machine off, regenerate its token or remove it under Settings, Machines & accounts.
Changing your password under Security & login also logs out every other browser, and their push notifications stop with them. The browser you changed it in stays signed in.
Signing in, signing up, logging out, password and address changes, ending a session, and changes to your name, plan or machines are only accepted from Termdeck's own pages. If another website makes your browser send one of them, the request is refused before it does anything.
What you are actually trusting
Two things, and it is worth naming them plainly rather than burying them.
The agent updates itself from the master
The master delivers updates, but it cannot write one. Every release is signed offline with a key the master never holds, and the agent installs a release only when the signature checks out, every file matches it, and the version is not older than the one it runs. A master that was broken into could hold updates back; it could not push its own code. Your first install is the exception: it trusts what it downloads, which is why the installer offers to print the whole source before running it. A bad release still rolls itself back and quarantines the version rather than needing hands on the machine.
If you want to inspect what your machines are running, the same files the installer downloads are readable at any time:
curl -fsSL https://termdeck.io/download/agent/manifest.json
curl -fsSL https://termdeck.io/download/agent/signature.json
curl -fsSL https://termdeck.io/download/agent/capabilities.js
The coding agent is the powerful part
Termdeck's own capability list is narrow. The coding agent it drives is not: an agent in full access mode can do anything you can do in a shell. That is what the permission system is for, and it is the layer that deserves your attention.
Prompts and approvals reach your machine through the master, so the master is also trusted to pass on only what you typed and what you approved. To take the unattended case off the table for a machine, set TERMDECK_FULL_ACCESS=off in its agent settings: it then refuses Full access whatever the master asks for, and every action waits for an approval. See Turning Full access off.
The refusals that do not depend on you
A set of guards refuse certain calls before any of your settings are consulted, and before the default read only auto allow list gets a look. The motivating case: Read is auto allowed because reads cannot mutate anything, and reading an SSH private key is a read.
Credential files, remote scripts piped into a shell, sudo, shell startup files, system paths, deleting outside the project, and broad recursive deletes are all refused by default. Each can be switched off individually, only from the settings page, and no auto allow rule can override one. Full detail on Approvals and permissions.
Notification actions
The Allow and Deny buttons on a push notification work without the app being open, so the notification carries its own authorisation: a single use token that expires after 5 minutes, when the prompt it answers is denied anyway, and is invalidated on first use. It authorises exactly one answer to exactly one pending prompt.
Account isolation
Every route is addressed by machine name and scoped to the owning account. Nothing is ever keyed on a bare session id, because session ids are generated locally and can collide between users. Message fan out is scoped the same way.
Rendering your code
File contents and diffs are built as document nodes, never as markup, on every path and for every file type. A file that contains HTML is displayed as text.
Practices worth adopting
- Open chats in the narrowest folder that contains the work. It bounds what the agent can reach and sharpens the risk scoring on every approval.
- Leave the hard denies on. They cost nothing on a normal day.
- Keep auto allow rules tight, with a specific glob and a low risk cap.
- Use full access only in a repository you would be happy to delete.
- Treat the machine token like an SSH key. Regenerate it if it has been anywhere it should not.
- Remove machines you no longer use, which revokes their tokens.
Reporting a vulnerability
Email [email protected] with the detail. Please do not open a public issue for a security report.