Dagger manual
Dagger runs AI agents on your machines and lets you drive them from a phone, a tablet, or another computer. Your code stays on your machine, and the service in the middle cannot read it.
What Dagger is
Three pieces work together:
| Piece | What it is | Where it runs |
|---|---|---|
| The app | What you look at: file browser, editor, terminal. | Your phone, tablet, or browser |
| The conductor | A small program that exposes folders on a machine and starts agents there. | Your desktop, laptop, or server |
| The relay | The meeting point that connects the two, so neither needs a public address. | dagger.cutthroatsoftware.com |
The conductor dials out to the relay, so it works from behind a home router, a corporate firewall, or a coffee-shop network with no port forwarding and no VPN. Everything between the app and the conductor is encrypted end to end: the relay passes sealed traffic it cannot open. See what we can see for exactly where that line falls.
Quick start
Five minutes from nothing to a terminal on your own machine.
- Create an account at dagger.cutthroatsoftware.com and confirm your email.
- Add a conductor: on Dagger Conductors, tap Add conductor, name the machine, and copy the command it gives you.
- Install it on that machine. One line, then the enrol command from step 2:
curl -fsSL https://dagger.cutthroatsoftware.com/install.sh | sh dagger enroll <your-token> - Expose a folder: in a project directory on that machine, run
dagger. - Open it: the machine appears on Dagger Conductors. Tap it, enter the pairing key once (see Pairing), and you're in.
Add the app to your home screen (iOS: Share, then Add to Home Screen. Android: Chrome ⋮, then Install). It launches full-screen like a native app and can show notifications.
Concepts
Conductor
One machine, one conductor. Installing it registers that machine with your account. A conductor can expose many folders, and it keeps running in the background, so your agents carry on working while your phone is in your pocket.
It runs natively on Linux and macOS, and on Windows inside WSL 2, where it drives native Windows toolchains (see Windows and WSL 2).
Folder (root)
A directory you have chosen to expose. The conductor can only see inside
those folders. This is the file jail, and it holds against symlinks and
.. tricks. Terminals are the one exception: see
Terminal & agents.
Device pairing
Each phone, tablet or browser you use is a device, and each one pairs with a conductor once. Pairing is what makes end-to-end encryption real: the key never passes through the relay.
Session
An open connection between one device and one conductor. Sessions are encrypted end to end and disappear when you leave. The shells you start inside them keep running on the machine.
Your account
An account is an email address and a password. Confirm the address before you go further: until you do, conductors cannot be added or opened, because an unconfirmed address cannot be recovered.
You can sign in with a password (plus a two-factor code if you've enabled one) or with a passkey, which uses Face ID, Touch ID, or whatever unlocks your device. Passkeys are easier and safer on a phone: tap the email field and your saved passkey is offered right there.
Account settings → Profile holds your display name (what team mates see instead of a bare address) and your email. Change password is there too, and asks for the current one. If you cannot get in at all, use Forgot password? on the sign-in screen and we email you a link.
If you use more than one account, Account settings → Connect another account keeps them side by side, and every account's conductors appear in one list. The accounts stay separate underneath: each opens its own conductors with its own credentials, and billing is per account.
Installing a conductor
Get the command from the app (Dagger Conductors, then the download
icon), where you also accept the End User License
Agreement that covers the conductor software. The one-liner installs the
binary to ~/.local/bin/dagger and registers it to start at login: a
systemd user service on Linux, a LaunchAgent on macOS.
curl -fsSL https://dagger.cutthroatsoftware.com/install.sh | sh
Then connect it to your account with the token from Add conductor:
dagger enroll <token>
The installer remembers which relay it came from, so the token alone is
enough. To move an already-enrolled machine to a different relay, run
dagger enroll again. It explains what will change and asks before
doing anything.
Windows
Install inside WSL 2 and run the one-liner there. Windows is a first-class target through WSL: your terminals drive Windows toolchains directly. See Windows and WSL 2.
macOS protected folders
Documents, Desktop and Downloads are gated by macOS privacy controls. The
first time the conductor reads one, macOS asks on that machine's screen,
so approve it there. To avoid the prompts entirely, add
~/.local/bin/dagger under System Settings → Privacy & Security →
Full Disk Access. If a folder in the app takes more than a few seconds to
open, that prompt is usually the reason, and the app will say so.
Windows and WSL 2
The conductor runs natively on Linux and macOS. Windows is the exception: running terminals needs a Unix pty, which Windows does not provide, so there the conductor runs inside WSL 2. That is not a lesser mode. WSL runs Windows executables directly, so a shell in Dagger can build, test and run native Windows software.
Setting it up
Five steps, all one-time. Steps 1 to 4 are ordinary WSL setup, and step 5 is Dagger.
1. Install WSL 2 and a distribution
In an administrator PowerShell:
wsl --install -d Ubuntu
If WSL is already installed, make sure it is current, because systemd support needs WSL 0.67.6 or newer:
wsl --version # if this isn't recognized, your WSL is too old
wsl --update
2. Turn on systemd
WSL runs systemd, so services work with the ordinary systemctl
commands, exactly as on a bare-metal machine. That is how the conductor starts
itself at boot and restarts if it ever dies. Recent Ubuntu images enable it
already; other distributions, and older installs, need it turned on by hand.
In your WSL terminal, edit (or create) /etc/wsl.conf:
sudo nano /etc/wsl.conf
Give it these two lines, then save and close (in nano: Ctrl+O, Enter, Ctrl+X):
[boot]
systemd=true
3. Restart the subsystem
The setting is read at boot, so WSL has to be shut down completely, not just have its window closed. Close the Linux terminal, then in PowerShell:
wsl --shutdown
Reopen your distribution and check that systemd is running the show:
systemctl list-unit-files --type=service | head
A list of services means systemd is active. An error such as
"System has not been booted with systemd as init system" means the
setting has not taken effect: check the spelling in
/etc/wsl.conf, confirm wsl --version reports 0.67.6 or
newer, and run wsl --shutdown again.
4. Keep it running when no terminal is open
A WSL distribution normally exists only while you have something open in it, and a conductor nobody can reach is a machine that shows as offline. Two settings fix that.
In the WSL terminal, let your services keep running after you log out:
loginctl enable-linger $USER
Then start the distribution with Windows itself. In Task Scheduler: Create Task, tick Run whether user is logged on or not if you want it up before you sign in, add a trigger At log on, and an action that starts a program:
Program: wsl.exe
Arguments: -d Ubuntu true
That command starts the distribution and exits. Starting the distribution starts systemd, and systemd starts the conductor.
5. Install Dagger
Back in the WSL terminal, the ordinary install:
curl -fsSL https://dagger.cutthroatsoftware.com/install.sh | sh
dagger enroll <token>
Check it the same way you would anywhere else:
systemctl --user status dagger
If you skip step 4, the conductor stops the moment you close your last WSL window, and the machine goes offline in the app. Step 4 is what makes a Windows conductor behave like the Linux and macOS ones.
Where to keep your code
WSL sees Windows drives at /mnt/c/…, and Windows sees WSL files at
\\wsl.localhost\Ubuntu\…. Both directions work, but crossing the
boundary is slow, so keep a project on the side that does the heavy lifting:
| Mostly built with | Keep the project in | Expose with |
|---|---|---|
| Linux tools (node, python, go, cargo) | The Linux filesystem, e.g. ~/projects/app | dagger in that folder |
| Windows tools (MSBuild, Visual Studio, Unity) | The Windows filesystem, e.g. C:\src\app | dagger --path /mnt/c/src/app |
Git on a Windows-side checkout is worth one setting, or you will see whole files appear modified when the line endings flip:
git config --global core.autocrlf input
Running Windows programs from a Dagger terminal
Anything ending in .exe runs from the WSL shell, with Windows'
PATH already appended to yours. From a terminal in the app:
dotnet.exe --version
msbuild.exe MyApp.sln -p:Configuration=Release
powershell.exe -NoProfile -Command "Get-Service -Name Spooler"
explorer.exe . # open this folder in Windows Explorer
Paths are the one thing to translate, since Windows programs want
C:\… and the WSL side wants /mnt/c/…. That is what
wslpath is for:
wslpath -w /mnt/c/src/app # C:\src\app
wslpath -u 'C:\src\app' # /mnt/c/src/app
Scripting a native Windows build
A build script lives in the repository, runs from a Dagger terminal, and produces a real Windows binary. Nothing about it is special to Dagger, which is the point.
#!/usr/bin/env bash
# build-windows.sh: native Windows build, driven from WSL.
set -euo pipefail
cd "$(dirname "$0")"
win_dir=$(wslpath -w "$PWD") # what the Windows tools need to be told
dotnet.exe restore "$win_dir\MyApp.sln"
dotnet.exe build "$win_dir\MyApp.sln" -c Release
dotnet.exe test "$win_dir\MyApp.Tests\MyApp.Tests.csproj" -c Release
echo "built:"
ls -la bin/Release
Run it from a terminal in the app (./build-windows.sh), and watch
the output stream to your phone like any other shell. Because the shell is
persistent, a long build keeps going when you put the phone down, and the
terminal bell tells you when it wants you (see
Bells & notifications).
If your project is Go, there is a second route that needs no Windows tooling at all, because Go cross-compiles:
GOOS=windows GOARCH=amd64 go build -o app.exe ./cmd/app
Letting an agent do it
An agent you start in a Dagger terminal is running in that WSL shell, so it
has the same reach you do: it can call dotnet.exe,
msbuild.exe and powershell.exe, read and write the
Windows filesystem under /mnt/c/…, and run the build script above.
Asking Claude Code to "build and test this on Windows" works because from where
it sits, that is just running a command.
Two things make that reliable, and both are worth doing before you hand a project to an agent:
- Put the build in a script, like the one above, and mention
it in your
CLAUDE.mdor README. An agent that can run./build-windows.shdoes not have to guess at MSBuild arguments or path translation. - Use
.rc.termfor the shortcuts. Aliases defined there exist in every terminal opened in that folder, for you and for the agent:# .rc.term alias build='./build-windows.sh' alias wintest='dotnet.exe test "$(wslpath -w "$PWD")\MyApp.Tests\MyApp.Tests.csproj"'
A container terminal (Sandboxing) cannot reach Windows programs: it only has the folder mounted into it. Use a host terminal on WSL when the build needs the Windows toolchain.
Exposing folders
Run dagger inside any directory on the machine to expose it:
cd ~/projects/my-app
dagger # expose this directory
dagger --list # what's exposed
dagger --remove my-app # stop exposing one
Exposed folders appear as the top level when you open that conductor. On a desktop you can also use the tray icon and pick a folder with the normal file dialog.
Pairing devices
The first time a device opens a conductor, it asks for that conductor's pairing key. This is the step that makes the encryption trustworthy: the key never reaches the relay, so nothing in the middle can insert itself between your device and your machine.
Your first device
Open a pairing window on the machine itself:
dagger pair open --gen --for 1h
It prints a key. Enter that in the app when prompted. On a desktop you can instead use the tray's Pair Client, which opens the window and copies the key for you.
Extra devices
From a device that is already paired, open the conductor's ⋮ menu and choose Pair a new device. You get the key as text and as a QR code, and the window closes itself after an hour.
Anyone with the key can add a device with full access
while the window is open. The key lives only in the conductor's memory,
never on disk and never at the relay, and you can close the window early with
dagger pair close.
Files & editing
Tap a conductor to browse it. You can open, edit and save files, create folders, rename and delete, and move files in and out with upload and download (transfers show progress and can be queued). Images, PDFs, SVGs and Markdown render in place.
The listing refreshes itself every few seconds, so files a build or an agent creates appear without a pull-to-refresh.
Opening a terminal before you have opened a folder starts it in your home directory, and so does opening one on a conductor that is not exposing anything yet. You are never locked out of a machine you own. The app explains this the first time and shows the commands; tick Don't show this again once you know them.
From that shell, dagger /path/to/folder exposes a folder, or
plain dagger exposes the one you are already in. It appears in the
file list right away, with nothing restarting and the shell still running.
Terminal help. The button at the right-hand end of the key bar
asks Dagger AI for a command. It knows which folder the shell is in, what branch
you are on, and what the shell just did, so "undo my last commit but keep the
changes" or "why did that fail" are questions it can answer rather than guess at.
Point it at a file with @, picking from a list as you type, or
+ to browse for one.
Two buttons sit in the corner of every answer. The terminal one, on the outside, puts the answer's command on your prompt without running it: you press Enter. The other copies that same command to your clipboard, for anywhere that isn't a shell. If an answer has no command in it, the copy takes the answer instead, and you can always select any part of one by hand. Each shell keeps its own conversation, so closing the panel and coming back finds it where you left it, half-typed question and all.
What it sends, and where that goes. The folder you are in and
your branch travel with the question. What a shell is showing does not,
unless you turn that on for the question — the button beside
+ — because a terminal often has a password or a token on it.
Files go when you name them with @ or +. Each question
shows exactly what went with it, and you can open it and read it.
Unlike everything else between the app and your machine, a question is not sealed end to end: it goes to Dagger AI through the relay, which can read it, and Dagger AI keeps it with the conversation until you delete your account. On a machine somebody has shared with you, nothing is attached automatically at all — only what you name — and the owner sees in their activity log that their machine's contents went to Dagger AI.
It is included in your plan. Terminal help draws no credit — a fast model answering in well under a second, covered by the subscription, with what it covered this month shown beside your balance. Credits are for the bigger jobs.
A whole folder downloads as a zip. Pick Download as
zip from the menu beside it and the conductor builds the archive as it
sends, so nothing is written on the far side and there is nothing to clean up.
The zip has one folder inside it, named after the one you asked for, so opening
it does not empty a hundred files into your downloads. Shortcuts and symlinks
are left out on purpose, along with anything the conductor could not read, and
a SKIPPED.txt inside the zip names every one of them. Very large
folders are refused rather than half-sent: the app holds the whole zip in memory
while it arrives.
Deleting asks you to type delete before the
button will work. There is no trash on the far side: what you remove is gone,
and removing a folder takes everything inside it. A button under your thumb is
too easy to press by accident for something that cannot be undone.
Ordering the file list
The button at the right of the path bar cycles how files are ordered: by name, by when they were last changed, or by when they were created, each forwards and backwards. Files sit above folders in every order, so "most recently changed" shows you the file you just touched rather than a stack of directories.
While a date order is chosen, each row shows that date next to its size, so you can see why the list is in the order it is.
Creation dates come from the filesystem, and not every filesystem keeps them. Where yours doesn't, those entries fall back to their modified date rather than pretending to be from 1970.
Terminal & agents
The terminal is where most of the work happens. You start an agent such as Claude Code in it, and answer that agent later from wherever you happen to be.
Open the terminal panel in a folder and a shell starts on that machine. Shells are persistent. Close the app, lose signal, switch devices: the shell keeps running and you reattach to it, scrollback and all. An agent can carry on working while your phone is in your pocket.
Typing on a phone
There's an in-app keyboard with the keys a terminal needs (Ctrl, Shift, Esc, arrows, Tab) that a phone keyboard hides.
Ctrl, Shift and Alt are sticky: tap one and it stays lit until the next key, so Ctrl+C is two taps rather than a chord your thumbs can't make.
The button next to the microphone cycles the bar through three pages, because more keys are useful than fit across a phone. It shows the page you are on:
- Keys: ^V to paste and ^C to interrupt whatever is running, the modifiers, Esc, Tab, arrows, Enter, Backspace.
- Nav: Home, End, Page Up, Page Down, Insert, Delete.
- Fn: F1 to F12, for programs like
htopthat use them.
The modifiers appear on every page, since those are the keys you press them with.
Three combinations are worth knowing. Shift+Tab cycles modes in Claude's CLI. Alt+Enter adds a line to a prompt without sending it. And Alt+B, Alt+F jump back and forward a word on the command line, with Alt+. pasting the last argument you used, which saves retyping a long path.
Several shells at once
A folder can have more than one shell open, listed in the terminal panel so you can switch between them: an agent working in one, a build in another. A shell you close stops on the machine, and closing it kills whatever was running, including an agent part way through a task, so Dagger asks first.
Ended shells keep their transcript. Pick one from the list to read back what happened after it finished, which is how you find out what an agent did while you were away. Clear ended sessions throws those away when you no longer want them.
Typing over a slow link
When your connection is slow enough to feel, Dagger draws each character as you type it rather than waiting for the machine to echo it back, so typing keeps up with you. On a fast connection it does not bother: the real echo arrives in a few hundredths of a second, and guessing ahead of it would only introduce corrections you can see. The switch is automatic and based on the round trip it measures while you type.
Keeping one shell in view
The terminal panel normally shows the shells opened in whichever folder you are browsing, so walking off to read code elsewhere swaps it. The pin button in the terminal panel, next to the folder button, holds it on the shell you are watching: browse anywhere and the agent stays on screen. Tap it again to follow the folder as usual. Opening a new shell moves the pin to it, since that is plainly the one you asked for, and closing the last shell in the pinned folder releases the pin.
Sandboxing
A shell normally runs as the user who started the conductor and is not limited to the exposed folder. It is a real login shell on your machine, which is what makes it useful and also what makes it worth thinking about.
Where that is too much, expose the folder as a container:
dagger --path ~/projects/my-app --terminal container
dagger --path ~/projects/my-app --terminal container --no-network
The shell then runs inside a container with only that folder mounted, so it
cannot read the rest of your disk, and --no-network takes its network
away as well. Use --terminal none to allow no terminals in a folder at
all, or enable_terminal: false in the config to turn host shells off
across the whole machine.
Folders you share are containers by default. Attach a team to a folder and, unless you set a mode yourself, that team's terminals there run in a container. Your own shells in the same folder stay ordinary host shells.
The reason is that a host shell runs as you and can read everything you can,
including the conductor's own identity key. Sharing a folder is meant to grant a
terminal in that folder, not the run of the machine, so the safer answer is the
one you get without asking. Set --terminal host deliberately if you
do want a team on a real shell; dagger --list shows the mode for
every shared folder and says plainly when a shared one is unconfined.
If the machine has no container runtime, terminals for that team are unavailable rather than unconfined.
A dockerfile.term in the folder chooses which
image the container terminal is built from, so an agent gets exactly the
toolchain that project needs. It does not switch the terminal into a container
on its own: the sandboxing comes from exposing the folder with
--terminal container, and a folder without that keeps a normal host
shell whatever Dockerfile is sitting in it. See
the .term files for the rest.
Per-project setup: the .term files
Three optional files let a project describe the terminal it wants. Put them in a folder you expose, commit them with the project if you like, and every terminal opened in that folder picks them up.
| File | What it does | Applies to |
|---|---|---|
.env.term | Environment variables for the shell. | Host and container terminals |
.rc.term | Shell startup: aliases, functions, a prompt. | Host and container terminals |
dockerfile.term | The image a container terminal is built from. | Container terminals only |
.env.term
A plain KEY=VALUE file, one per line. # comments, a
leading export, and quotes around values are all accepted and
ignored. The variables are set in every shell started in that folder.
# .env.term
PORT=5173
DATABASE_URL="postgres://localhost/dev"
export RUST_LOG=debug
PORT is the one variable Dagger reads for itself: the tailnet panel
uses it to offer a link to the service this project serves, so you can open it
from your phone without remembering the number.
Treat .env.term as a convenience for
development settings, not a secret store. Anyone who can open a terminal in
that folder gets these variables, and a file committed to a repository is a
file that has left your machine.
.rc.term
Shell code sourced into each new terminal in that folder, like an extra
.bashrc that belongs to the project. Use it for the things an
environment file cannot express.
# .rc.term
alias t='npm test'
alias serve='npm run dev'
gitclean() { git clean -xdf "$@"; }
PS1='my-app $ '
It is handed to the shell through the shell's own startup, so nothing is
typed into your terminal and nothing lands in your history: bash gets it as an
extra rc file, the sh family through $ENV, and zsh through its own
config directory (your .zshenv and .zshrc are sourced
first, so your setup is untouched).
dockerfile.term
An ordinary Dockerfile that builds the image for this folder's container terminal, so an agent gets exactly the toolchain the project needs.
# dockerfile.term
FROM node:22-alpine
RUN apk add --no-cache git ripgrep
It applies only when the folder is exposed with
--terminal container (see Sandboxing). On a
host-shell folder the file is simply ignored, and the shell is a normal login
shell on your machine.
The image is built the first time a terminal opens and reused afterwards. Edit the Dockerfile and a rebuild button appears on the terminal panel for that folder. A broken Dockerfile is deliberately not fatal: Dagger falls back to the last image that worked, or to a plain Alpine shell, so a typo cannot lock you out of the terminal you would use to fix it.
Bells & notifications
Dagger tells you when a machine needs you. Two things count as needing you, because neither one catches the other.
A program rang the bell. Claude Code does this when it wants an answer. It happens while the program is still running, which is why it is the only signal an agent can send: it never gets back to a prompt to say anything else.
A command finished and the shell went quiet. Most things that
end never ring: a build, a test run, a migration, an agent that exited on its
own. Dagger notices the shell coming back to its prompt and tells you, but only
when the thing that ran took more than a minute and nobody has typed in that
shell for a while. Somebody who pressed a key seconds ago is sitting right
there and does not need telling, which is what keeps git commit
closing its editor, or sudo taking a password, from pestering
you.
When either happens:
- Push notification to your phone or desktop, deep-linked: tapping it opens that exact shell. On Android it also carries a What changed button, which opens the git panel on that folder — at 7am the diff is usually what you want, not the transcript. Both buttons open the app: a notification cannot answer a prompt or run anything by itself, because your terminal traffic is encrypted between your device and your machine and nothing in between can act on it. iOS shows no buttons and a plain tap does the same as Open.
- In-app alert with a chime, for the clients push can't reach. It offers Open to jump straight to the shell, and an x to send it away, which is what you want when it lands on top of the keyboard.
- Email, if you want it.
The second one needs your shell to say when it is at a prompt, which Dagger
arranges by itself for bash, zsh and the sh family. fish, an unknown shell, or a
shell inside tmux cannot say, so those report nothing rather than guessing, and
the shell list shows no state for them. If you would rather Dagger left your
prompt alone entirely, set DAGGER_NO_SHELL_MARKS=1.
The shell list shows the same thing at a glance: waiting for you 4m when something running has asked a question, idle for a shell sitting at a prompt, running 12s while it works, or the exit code of something that failed.
A shell that rang is marked in the list, on whichever machine it rang on, so "which one wants me" is answered where you go to pick one. Going to it puts the bell away, and so does reading its output or killing it. Nothing else does: the mark stays until you have actually been.
Choose which events reach you, on which channel, in Account settings → Alerts. Test alerts sends a real one on every channel so you know it works before you rely on it. Dagger stays quiet about the shell you're currently looking at, since being told about the thing on your screen is just noise.
Turning on push
Push needs permission from your browser or phone, so enable it from Account settings on each device you want alerts on. On iOS, add the app to your home screen first, because Apple only allows notifications for installed web apps.
Search, git, and tailnet
Search runs on the machine (ripgrep when it's installed), respects
.gitignore by default, and offers match-case and whole-word toggles.
Results stream in as they're found; arrows cycle through them without losing
your place.
Git: a panel showing history for the repo containing the current folder, the files each commit touched, and side-by-side diffs. A diff opens as the changed hunks; the Whole file button shows the same change with the rest of the file around it, which is usually what you need to judge one.
Tailnet: if the machine runs Tailscale, a status view showing the
machine's tailnet address. Useful when a project serves a port you want to open
from your phone, and it reads PORT from the folder's
.env.term so the link is ready to tap.
Teams
Teams share specific folders on your machine with other Dagger accounts. A colleague gets the folders you name, with the permissions you set: read, write, terminal, or any combination.
dagger team set eng --read --write # create/configure a team
dagger <dir> --teams eng # share a folder with it
dagger team open eng --gen --for 24h # a pairing key for members
dagger team list # who has what
Members pair with the team's key, which grants only that team's folders and permissions, never your whole machine. Removing someone from the team drops their device's access on that machine automatically.
A team member's session runs on your machine. Encryption protects them from the network and the relay, not from the computer itself. The same is true in reverse when you join someone else's team. Share with people you would hand a laptop to.
Security
Two-factor authentication
Account settings → Security turns on TOTP with any authenticator app, and gives you recovery codes. Store those somewhere other than the phone with the authenticator on it.
If you lose your authenticator
In order of ease:
- Enter one of your recovery codes where the six-digit code is asked for. They work at sign-in and during a password reset, and each is usable once.
- Sign in with a passkey on a device that already has one. A passkey is itself a second factor, so no code is needed.
- If both are gone, email support@cutthroatsoftware.com. We can turn two-factor off after verifying who you are, which signs your sessions out and emails the account. Set it up again straight afterwards.
Resetting your password does not get you past two-factor: the reset form asks for a code as well, or the mailbox alone would be enough to open a protected account. Getting the code wrong does not spend the emailed link, so you can try again without asking for another.
Passkeys
Register a passkey per device for one-tap biometric sign-in. Passkeys can't be phished or reused across sites, and on iOS/Android they sync through your platform account so a new device inherits them.
Adding one asks for your password, because a passkey keeps working after a password change and so is worth more than a borrowed session. You are emailed whenever a passkey is added. Signing in with one asks for nothing but your face, finger or device unlock.
Auto sign-out
Dagger can return to the login screen after a period of inactivity. Set it under Account settings → Auto sign-out, from 5 to 60 minutes, or leave it off. It is a per-device setting, so you can be strict on the tablet that lives on the kitchen table and relaxed on the desktop in a locked room. Shells on the machine keep running. Only the screen locks.
Locking enrollment
Once your machines are set up, turn off Allow new conductor enrollment in Account settings → Security. No new machine can then be attached to your account, and switching it back on costs your password. It is a cheap way to make a stolen session much less useful.
Which devices get your alerts
Account settings → Devices lists the browsers registered for push, with the one you are using marked. Remove any you no longer use, and it stops receiving notifications.
Renaming a machine
The name is just a label you chose when you enrolled it. Pick Rename conductor from the ⋮ menu on its row to change it. The machine itself is not involved, so the new name survives restarts and reconnects, and entries already in your activity log keep the name they were recorded under.
Devices and revocation
Pairing keys are memory-only and time-limited. To retire a machine entirely, use Remove conductor from its ⋮ menu: the account forgets it, its shared folders leave their teams, and paired devices lose access. Nothing on the machine itself is touched, and you can enrol it again later.
Plans & billing
Plans differ by how many conductors you can enrol and whether you can share: Solo for personal machines, Pro to invite and link accounts, Team for named teams with per-member permissions. Every plan starts with a free trial, and you can cancel any time from Account settings → Subscription, which opens the billing portal for invoices and payment details.
If a payment fails, access continues for a grace period while your bank and Stripe retry. Dagger will not cut off an agent mid-run over an expired card.
Deleting your account
Account settings → Delete account removes the account, its conductors,
teams and history, and cancels any subscription immediately. It asks for your
password (and a two-factor code if you use one), because a live session on its
own should not be able to destroy an account. It cannot be undone, and the
address is free to register again afterwards. Nothing on your own machines is
touched: uninstall the conductor there with dagger uninstall.
Activity log
Account settings → Activity shows what happened on your account: machines connecting, sessions opening, terminals starting, devices pairing, each with who did it, on which machine, and from where. On shared folders it names the team member, so a conductor owner can always see who has been on their machine.
Conductor CLI reference
Run dagger help on the machine for the current list.
| Command | What it does |
|---|---|
dagger | Expose the current directory. |
dagger --path <dir> | Expose a specific directory. |
dagger --list | List exposed directories. |
dagger --remove <name> | Stop exposing one. |
dagger --terminal host|container|none | Terminal mode for the directory being added. |
dagger --image <image> | Container image for that directory's terminals. |
dagger --no-network | Container terminals here get no network. |
dagger install | Install the binary and the login service. |
dagger enroll [url] <token> | Connect this machine to your account. |
dagger refresh | Restart the conductor to apply config changes. Running shells end. |
dagger upgrade | Update the conductor to the latest build, keeping every running shell. |
dagger pair open --gen --for 1h | Open a pairing window and print a key. |
dagger pair status|close | Check or close the pairing window. |
dagger team … | Teams: set open close list remove rotate. |
dagger uninstall | Remove the service and binary; config and state stay. |
Configuration lives in ~/.config/dagger/config.yaml (macOS:
~/Library/Application Support/dagger/config.yaml). Edit it, then run
dagger refresh.
Updating a conductor
Press Update next to the machine in the app, or run
dagger upgrade on the machine itself. They do the same thing: the
conductor downloads the new build, checks it against the checksum the relay
publishes, runs it to confirm it starts, and only then replaces itself.
Your shells keep running, including anything working in them. The conductor swaps its own program without becoming a new process, so the shells never notice and the system service never restarts. The app's connection drops for a moment and comes back on its own.
Two things do change. Terminal scrollback starts fresh, because it is rebuilt from the screen rather than stored; the full transcript is untouched and history still has everything. And a terminal redrawing at that exact moment can look garbled until its next full redraw.
If something cannot be carried across, the upgrade refuses and names it, and nothing is changed. Close that terminal and run it again.
An older conductor that predates this shows too old to update itself in the app instead of a button. Run the install command on that machine once; every update after it is the painless kind.
The tray
On Linux and macOS desktops the conductor puts an icon in the menu bar or system tray: a dot showing whether it's connected, the folders it exposes (click one to open it in the app), Add Directory with the normal folder picker, Remove Directory, and Pair Client, which opens a pairing window and copies the key.
On Linux the tray runs with your desktop session, so it appears after you log in. Stock GNOME needs the AppIndicator extension (Ubuntu ships it). A headless server has no tray at all, and everything remains available from the CLI.
Troubleshooting
The machine says "Pending enrollment"
The conductor hasn't connected yet. On that machine check the service:
systemctl --user status dagger # Linux
launchctl print gui/$(id -u)/com.cutthroatsoftware.dagger # macOS
"another dagger daemon is already running" means a conductor started by hand is holding the socket. Stop that process and the service takes over.
"That pairing key doesn't match"
Keys are per conductor and expire. Open a fresh window
(dagger pair open --gen) and use the key it prints now. A key from an
earlier window, or from before the conductor restarted, will not work.
A folder takes forever to open (macOS)
Almost always the privacy prompt waiting on that machine's screen. See macOS protected folders.
No notifications
Enable push per device in Account settings. Test alerts tells you which channels went out, and if it says it pushed to your browsers then the message left the relay and was accepted — so anything still missing is on the device.
Check Focus first. On iPhone, a Focus — Sleep especially, which turns itself on every night — silences a web app's notifications completely, with nothing to see anywhere. Nothing about the app or the site will look wrong. Add Dagger to that Focus's allowed list, or turn the Focus off to test. The same goes for Do Not Disturb on any OS, and Firefox has its own "pause notifications" switch.
If Focus is not it: on iOS the app must be on your Home Screen and opened from there, and it gets its own entry in Settings → Notifications once permission is granted. Check it is allowed there, and look in Notification Center in case alerts are arriving quietly rather than as banners.
The app looks out of date
Reload the page. Dagger does not cache the app, but iOS Safari holds on hard, so pull down to refresh, or close and reopen the installed app.
What we can see
This is the reason to use Dagger at all, so here it is plainly.
The relay never sees your content. File contents, edits, terminal input and output, and pairing keys are encrypted between your device and your machine. The relay forwards sealed traffic; a compromised relay can't read it or impersonate your machine.
The relay does see metadata, because it needs it to do its job: your email address, the names of your conductors, when they connect, team membership, IP addresses, and the folder name a bell came from (without which a notification could not tell you which shell wants you).
Your code never leaves your machine unless you download it. Dagger copies nothing to the cloud; there is no repository of your files anywhere but where they already were.
See the Privacy Policy and Terms of Service for the legal statement of the same.
Help & support
Stuck, or found something broken? Email support@cutthroatsoftware.com and we will help.
Two things make a problem much faster to sort out, so include them if you can: what you were doing when it happened, and which machine and device were involved (the conductor's name, and whether you were on a phone, a tablet or a browser). If the app showed an error, the exact wording of it is worth more than a description of it.
Never send us a pairing key, a password, or the contents of
a .env.term. We never need them, and support will never ask.