Skip to content
All guides

USER GUIDE

SSH agent

Use the SSH keys in your vault on a Linux machine without copying them there. Your phone or the web app approves each signature.

How it works

cryptex-agent is an SSH agent for Linux that holds no private keys. When ssh, git or ssh-keygen asks it to sign, it sends the request to the device you paired it with: the Android app or the web app. You approve there, your vault signs, and only the signature goes back to the machine.

The private key never reaches the Linux machine, so malware on that machine can't copy it. The worst it can do is ask for a signature, and you see each request with the server, the program and the key before anything is signed.

Requests travel through a relay, either the hosted one that comes with Online Services or one you run yourself. They are end-to-end encrypted between the agent and your device. The relay sees which agent talks to which device, when and how much, and nothing else.

You need an SSH key item in your vault, and the agent needs a Linux machine with systemd. Release builds exist for amd64 and arm64.

Install the agent

Download a release tarball from the GitHub releases. Agent releases are tagged cryptex-agent-v followed by the version. An AUR package for Arch Linux, cryptex-agent-bin, will follow.

Shell
# The version you picked on the releases page, and amd64 or arm64
VERSION=REPLACE_WITH_VERSION
ARCH=amd64
NAME=cryptex-agent-$VERSION-linux-$ARCH
BASE=https://github.com/CryptexIndustries/vault-web/releases/download/cryptex-agent-v$VERSION
curl --fail --location --remote-name "$BASE/$NAME.tar.gz"
curl --fail --location --remote-name "$BASE/sha256sums.txt"
# Stop here unless this prints OK
sha256sum --check --ignore-missing sha256sums.txt
tar -xzf "$NAME.tar.gz"
sudo install -Dm755 "$NAME/cryptex-agent" /usr/bin/cryptex-agent
sudo install -Dm644 "$NAME/cryptex-agent.service" /usr/lib/systemd/user/cryptex-agent.service

To install without root, copy the binary to ~/.local/bin and the unit file to ~/.config/systemd/user/, then change the unit's ExecStart= line to %h/.local/bin/cryptex-agent run.

Pair it with your vault

Pairing tells the agent which device approves its requests and which keys it may ask for. Run this on the Linux machine:

Shell
cryptex-agent pair

It prints a QR code and, under it, a text code starting with cryptex-pair:. The code works for three minutes. If it expires, press Enter in the terminal for a new one. On a light terminal background, use cryptex-agent pair --invert so the QR code scans.

With the Android app

  1. 01

    Open the Devices tab, tap + next to the search field, and choose Connect an agent.

  2. 02

    Point the camera at the QR code. If the camera can't read it, tap Paste code and paste the text code.

  3. 03

    Give the agent a name and pick the keys it may use under Keys this agent can use, or none and add them later with Edit keys. With many keys, search the list or use Select all. Then tap Connect. On Android 13 and later, the phone asks whether Cryptex Vault may send notifications. Allow it, or requests that arrive while the app is closed or locked can't reach you.

  4. 04

    The phone shows a short code such as CP22-34W6. Check that the terminal shows the same one, and press Enter there.

Android Devices tab with the Add sheet open, listing Create invitation, Use invitation and Connect an agentView full size
On the Devices tab, tap + next to the search field, then Connect an agent.
Android Connect an agent screen showing the machine work-laptop, Linux amd64, the name work-laptop and two selected keys, GitHub and Deploy, above a Connect buttonView full size
Tap each key the agent may use, then Connect. The machine line is what the machine reports about itself.
Android Check the code screen with a confirmation code, Waiting for the agent and a Cancel buttonView full size
Compare this code with the terminal on work-laptop, then press Enter there.

With the web app

  1. 01

    Unlock your vault. In the sidebar, under Linked Devices, find Connected Agents and choose +.

  2. 02

    Paste the text code from the terminal and choose Continue.

  3. 03

    Give the agent a name, pick its keys and choose Connect. With many keys, search the list or use Select all. You can also connect with no keys and add them later with Edit keys.

  4. 04

    Check that the terminal shows the same code as the browser, and press Enter there.

Vault dashboard with the Connected Agents heading in the sidebar under Linked Devices, with a gear and a + buttonView full size
Connected Agents sits in the sidebar under Linked Devices. Choose + to connect an agent. The gear opens Agent relays.
Connect an agent dialog with a pasted cryptex-pair code and a Continue buttonView full size
Paste the code that cryptex-agent pair printed, then choose Continue.
Connect an agent dialog showing the machine work-laptop, Linux amd64, a Name field and two selected SSH keys, GitHub and DeployView full size
Name the agent and tick the keys it may use, then choose Connect. The machine line is what the machine reports about itself.
Check the code dialog showing a confirmation code and Waiting for the agentView full size
Compare this code with the terminal, then press Enter in the terminal. Once the save starts, Cancel shows Saving…

When you connect your first agent, the browser asks whether Cryptex Vault may show notifications. Allow it to hear about requests while the tab is in the background.

After you press Enter, your device saves the agent in your vault. From then on Cancel shows Saving… and can't be pressed until the save is done.

The device you pair with becomes the agent's approver. Only that device answers its requests. Your other linked devices see the agent and its history, and can revoke it.

The agent uses the hosted relay unless you pass --relay. For a relay you run yourself, use cryptex-agent pair --relay https://relay.example.com. The app or browser then asks for the relay's access token and saves it in your vault for that device. See the relay below.

Use it with ssh and git

Start the service and point SSH at the agent's socket:

Shell
# Start the agent now and at every login
systemctl --user enable --now cryptex-agent
# Point SSH at it; add this line to ~/.bashrc or ~/.zshrc too
export SSH_AUTH_SOCK=$XDG_RUNTIME_DIR/cryptex-agent.sock
# Lists the keys you granted, even offline
ssh-add -L
cryptex-agent status

ssh-add -L should print the public keys you granted. From then on, ssh, scp, git push and anything else that uses SSH_AUTH_SOCK asks your device to approve each login. Each request waits up to 90 seconds. If it isn't answered within a few seconds and notify-send is installed, the machine shows a desktop notification as a reminder.

The agent logs each request it is waiting on, and how it ended, in journalctl --user -u cryptex-agent. To also see a line in the terminal that made the request, for example on a server without a desktop, set terminal = true in ~/.config/cryptex-agent/config.

Some desktops set SSH_AUTH_SOCK to their own agent after your shell starts. To make ssh use this agent whatever the variable says, add IdentityAgent ${XDG_RUNTIME_DIR}/cryptex-agent.sock under Host * in ~/.ssh/config.

With several keys granted, the agent offers the key that last worked with a server first, so you approve one request per login.

Sign git commits

Git can sign commits with an SSH key. Copy the public key from the SSH key item in your vault, or from ssh-add -L, and set it as the signing key:

Shell
git config --global gpg.format ssh
# Paste the public key from the SSH key item, after key::
git config --global user.signingkey "key::ssh-ed25519 AAAAC3Nza... GitHub"
git commit -S -m "Signed commit"
# Optional: sign every commit
git config --global commit.gpgsign true

Each signature asks for approval like a login does. For GitHub or GitLab to show commits as verified, add the same public key there as a signing key.

The agent can't add, remove or lock keys, because it has none. ssh-add with a key file, ssh-add -d, ssh-add -D and ssh-add -x are refused.

Approve requests

A request shows a question such as Sign in to github.com? and these details:

  • Agent: the name you gave the machine.
  • Server and Host key: where you are logging in.
  • User: the account on the server.
  • Key: the vault item that will sign.
  • Process: the command that asked, such as git push, and its process ID.

The process and the machine name come from the machine itself. Treat them as a hint, because malware on the machine could fake them.

When several requests wait, the prompt shows which one you are looking at, such as 1 of 3, and moves on to the next after each answer. Approve can't be used for about 0.7 seconds after a request appears, so a tap or key press meant for something else can't approve it. In the web app, the prompt never opens with Approve focused, so pressing Enter can't approve it either.

Request dialog titled Sign in to git.example.com with 1 of 2 waiting, agent work-laptop, user git, a verified host key, the GitHub key, the process git push origin main, Remember choices, Pause agent, Deny and ApproveView full size
A login request in the web app. The host key is verified, and the server name matches one you approved before. Pause agent at the bottom stops all requests from this agent.

Verified servers

OpenSSH 8.9 and later tell the agent which server they are logging in to, signed by that server's host key. Your device checks that signature itself and marks the host key verified. The server name comes from the machine's known_hosts file, so it is only what the machine reports.

Each agent remembers the server name it showed for each host key when you approved a login yourself. These names sync with the agent to your other devices. When the name and host key match one you approved before, the server line says You approved this name for this host key before. Otherwise it says Name reported by the machine, not verified. The prompt warns when a known host key comes with a new name, or a known name with a new host key. Approve those only if you expected the change, for example after the server was reinstalled.

Request dialog for deploy.example.com with a warning that the machine now calls this server deploy.example.com and before it was git.example.comView full size
The same host key arrived under a new name. Deny unless you renamed the server yourself.

Destination unknown

Some clients don't say which server they log in to: OpenSSH before 8.9, Paramiko and therefore Ansible, libssh2, and programs written in Go. Their requests show SSH login, destination unknown. Deny is the default button, the other choice is Approve once, and these requests are never remembered. Approve one only if you started that login yourself a moment ago.

Agent forwarding

Forwarding works: after ssh -A to a jump host, logins from there to other servers still ask your device. A forwarded request carries a warning and a Via line naming the jump host. It means a server you are logged in to is asking to use your key, so approve it only if you expect it. Forwarded requests are never remembered.

Signatures

Commit signing shows Sign a git commit? with the namespace git and the folder the command ran in. Your device sees only a hash of the commit, not its contents, and can't check which repository it belongs to. Other ssh-keygen -Y sign requests show a title such as Sign a file?.

Remember an approval

For verified logins and git signatures, the Remember section lets you skip the next prompts until the vault locks:

  • Only this request, the default.
  • This key for one program on this agent, such as ssh on your workstation.
  • This key for this server.
  • For git signatures, this key for the running git process, so a rebase that signs many commits asks once.

Remembered approvals live only in the approving device's memory. They have no time limit and end when its vault locks. The history marks them Approved automatically. Forwarded requests, unknown destinations and signatures other than git commits always ask.

Locking the vault stops signing at once, even while the vault is still being saved.

Pause an agent

If you didn't expect a request, choose Pause agent at the bottom of the prompt. On Android the button names the agent, such as Pause work-laptop. That refuses the request in front of you too. While an agent is paused, its requests are refused without a prompt and its remembered approvals don't apply. The pause works at once on the device you used and syncs to your other devices with the vault.

You can also pause from the agent's page: open the menu at the top, next to Edit keys, and choose Pause agent. The page then shows Paused with a Resume button. After you resume, requests ask again.

Agent page for work-laptop with a Paused notice and a Resume button above the approver, machine, keys and recent requestsView full size
A paused agent. Choose Resume to get its prompts back.

On Android

While your phone approves at least one agent, Android shows a permanent Ready for SSH requests notification. That keeps the app connected in the background, also after a restart. A request shows as SSH request from followed by the agent name, with Review and Deny. While the phone is locked or its screen is off, the notification says only Approval needed, also in the notification shade. The agent name comes back when you unlock the phone. Deny works without unlocking. Review asks you to unlock the vault and then opens the request. If the app is open and unlocked, the request opens straight away.

While notifications are off for Cryptex Vault, the Devices tab and the agent's page show a notice with Open notification settings.

Android notification reading SSH request from work-laptop, Tap to review it, with Review and Deny buttonsView full size
A request while the phone is unlocked. Review opens it in the app, and Deny answers without opening the app.
Android lock screen with a notification that reads only Approval neededView full size
The same kind of request on a locked phone. The notification doesn't name the agent until you unlock.
Android SSH request screen showing 1 of 2, Sign in to git.example.com, agent work-laptop, a server name approved before, a verified host key, user git, the GitHub key and the process git push origin main, with Deny and ApproveView full size
The approval screen on Android. Approve works about 0.7 seconds after the screen opens.
Lower part of the Android SSH request screen with the Remember choices and a Pause work-laptop buttonView full size
Scroll down for the Remember choices and the button that pauses this agent.

In the web app

The browser approves only while your vault is unlocked and a vault tab is open. Requests appear as a dialog over the vault. Closing the dialog denies the request. When the tab is in the background, its title shows how many requests are waiting, and a browser notification appears if you allowed them.

Your vault can be open in only one tab at a time. Another tab shows Your vault is open in another tab with a Use here button. It locks the vault in the first tab, which saves first, and you then unlock it in the new tab. Leaving the vault for another page of the site also locks it, after saving.

Card reading Your vault is open in another tab, with a Use here buttonView full size
A second tab. Use here locks the vault in the other tab, after saving, so you can unlock it here.

Manage and revoke agents

On Android, agents are listed under Connected agents in the List view of the Devices tab. In the web app, they are under Connected Agents in the sidebar. Open one to see its approver, machine, relay, keys and Recent requests. The history keeps up to 200 requests from the last 90 days, and it syncs to your linked devices with the rest of the vault.

Android Devices tab in the List view with a Connected agents section listing work-laptop, 2 keys, and a gear buttonView full size
On Android, switch the Devices tab to List. Tap the agent to open it. The gear next to Connected agents opens Agent relays.
Android agent page for work-laptop with the pairing date under its name, the approver, the relay, a closed Keys section showing 2, and recent git signatures, one of them deniedView full size
The agent page on Android. Edit keys is at the top. The menu button next to it has Pause agent and Revoke agent. Tap Keys to see which keys the agent may use.
Agent page for work-laptop with approver, machine, paired date, relay, two keys and recent requests, and the menu next to Edit keys open with Pause agent and Revoke agentView full size
Choose the agent under Connected Agents to open its page. Edit keys is at the top. The menu button next to it has Pause agent and Revoke agent.

To change which keys an agent may use, open it on the approving device and choose Edit keys. The agent gets the new list the next time it connects. If it can't save the list to disk, it signs nothing, reconnects and tries again.

Revoke an agent

Open the agent, open the menu at the top and choose Revoke agent, in the web app or on Android. Any of your linked devices can do this. On the machine itself, run:

Android agent page with a sheet for work-laptop offering Pause agent and Revoke agentView full size
The menu on the agent page on Android, with Pause agent and Revoke agent.
Shell
cryptex-agent unpair

A revoked agent lists no keys and signs nothing until you pair it again. If the agent uses a relay you run yourself, revoke it from the approving device or with cryptex-agent unpair.

To pair a machine with a different device, run cryptex-agent pair --force. It replaces the existing pairing.

The relay

The agent and your device never connect directly. Both keep a connection open to a relay, which passes the encrypted requests and answers between them. The relay can't read them or sign anything. It sees which agent talks to which device, when, and how much.

  • Hosted relay. relay.cryptex-vault.com comes with Online Services. The approving device must be signed in to Online Services. cryptex-agent pair uses it by default.
  • Your own relay. The relay is open source. Run it with an access token and pair with --relay. The self-hosting guide adds it to the self-hosted stack.

Each agent keeps the relay it was paired on. To see or change the access tokens a device has saved, open Agent relays. In the web app, that is the gear next to Connected Agents. On Android, it is the gear next to Connected agents in the List view of the Devices tab. Before you connect an agent, tap + on the Devices tab, choose Create invitation, then Advanced connection and Manage custom servers. The SSH agents section of that screen has Agent relays.

Android Agent relays screen with the hosted relay from Online Services, one own relay with its access token saved, an Add relay button and Battery settingsView full size
Agent relays on Android. Tap a relay of your own to change its access token. Battery settings lets the app keep running in the background.

A relay address must start with https://. Plain http:// works only for a relay on the same machine (localhost, 127.0.0.1 or [::1]), for testing. The agent, the Android app and the web app all refuse other http:// addresses. The agent also doesn't follow redirects, so a relay can't send it to another server.

Pass the relay's address exactly as the relay knows it (its RELAY_PUBLIC_URL). Sign-ins are checked against that address, so localhost fails on a relay set up as 127.0.0.1, even on the same machine. The app then reports the pairing code as expired.

To use a relay other than the hosted one by default, set relay = https://relay.example.com in ~/.config/cryptex-agent/config.

Troubleshooting

No request arrives

  • Run cryptex-agent status. It shows the pairing, the granted keys and whether the service is running. journalctl --user -u cryptex-agent shows relay connection errors.
  • On Android, check that notifications are allowed for Cryptex Vault in the system settings, and that the Ready for SSH requests notification is there. If requests arrive late or not at all while the phone is idle, open Battery settings on the Agent relays screen and let Cryptex Vault run in the background.
  • In the web app, unlock the vault and keep a vault tab open. If Chrome discarded the tab, switch to it once.
  • Requests go to the device that approves the agent. The agent's page shows which one under Approver.

The pairing code doesn't work

Codes expire after three minutes and work once. Press Enter in the terminal for a new code, or run cryptex-agent pair again. If the device says the code is for your own relay, enter that relay's access token.

ssh-add -L lists nothing

  • The agent has no identities means the agent isn't paired, was revoked, or has no keys granted. cryptex-agent status says which. Pair again, or use Edit keys on the approving device.
  • Could not open a connection or Error connecting to agent means SSH_AUTH_SOCK isn't set in this shell, or the service isn't running. Run systemctl --user enable --now cryptex-agent and export the variable again.
  • If it lists other keys, SSH_AUTH_SOCK points at another agent.

agent refused operation

ssh prints this when the agent didn't get a signature. Usually the request was denied, or nobody answered within 90 seconds. It also happens when the key isn't granted to this agent anymore, the agent was revoked, or the device can't sign with that key type. The agent logs the reason: journalctl --user -u cryptex-agent shows a Signature not given line with it.