The ssh-gateway package
The ssh-gateway package is the SSH gateway on its own, with the product name SSH Gateway: a local server with a web UI and an MCP server through which your AI agent runs commands and transfers files on your SSH hosts, each action approved by you or by a rule you set. It is free and open source under the Apache-2.0 licence, and its command is ssh-gateway.
The package holds projects and everything the SSH gateway describes: connections, approvals and rules, commands, files and jobs, health checks and the history. It has no specifications, ToDo cards, plugins or plugin library. The athenode package already contains the SSH gateway, so install ssh-gateway only when you want the gateway without the rest of Athenode. To move from ssh-gateway to the whole of Athenode, stop ssh-gateway and install Athenode.
Requirements for ssh-gateway
- Node.js 22.16 or later. Check with
node --version. - macOS, Linux or Windows.
- 300 to 700 MB of free memory. Docker and a database server are not needed.
Install and start ssh-gateway
Run ssh-gateway without installing it, or install it globally when you want to run it as a service.
Start the gateway with
npx:npx ssh-gatewayOr install it globally and start it:
npm install -g ssh-gatewayssh-gatewayRead what the gateway prints: "SSH Gateway" with its version and "is running.", the address of the web UI, the address of the MCP server and the data folder.
On the first start, back up the master key file that the line "Created a new master key" names, as back up the data of ssh-gateway describes.
Open
http://127.0.0.1:3717in your browser and create a project.Add a connection to the project and trust its host key, as the SSH gateway describes.
Connect your AI tool to the project, as connect an AI tool describes.
ssh-gateway without a command is ssh-gateway start. The gateway runs until you stop it with Ctrl+C in its terminal or with ssh-gateway stop from another one.
Option of start |
What it does |
|---|---|
--port <n> |
Listens on another port of 127.0.0.1, instead of 3717. |
--data-dir <dir> |
Keeps the data in another folder, instead of ~/.athenode. |
--open |
Opens the web UI in your browser. When the gateway already runs on the data folder, opens its web UI and exits. |
One gateway runs per data folder. A second start on the same folder says that SSH Gateway is already running, with its address and process id, and exits with 1. When the port is taken by another program, the start fails with "is already in use by another program" and suggests --port <n>.
The gateway listens on 127.0.0.1 only and answers only requests addressed to 127.0.0.1 or localhost on its port, or to the host of its public URL. It asks for no sign-in, so any program of any user on your machine can use it. Run it on a machine whose users and programs you trust. For an AI tool in a container, set the address the container uses as publicUrl in config.json of the data folder, restart the gateway and forward its port, as the daemon's configuration describes: the gateway takes the same key. A team that shares its connections uses Athenode Pro, as the gateway on Athenode Pro describes.
Connect an AI tool to ssh-gateway
An MCP session works in one project, so the address your AI tool uses names the project with ?project=<id>. Copy the project's id from its settings page in the web UI.
Print the snippets for your AI tools:
ssh-gateway mcp-config --project <id>Add the snippet of your tool to its MCP configuration, or run the command it gives for Claude Code.
mcp-config prints snippets for Claude Code, Cursor, VS Code and other clients that use the Streamable HTTP transport, with the server name ssh-gateway. --client followed by claude-code, cursor, vscode or json prints one of them only. Without --project, the address holds the placeholder <PROJECT_ID>, which you replace with the id. mcp-config writes no file, and the gateway must be running for the tool to connect. For Claude Code the command is:
claude mcp add --transport http ssh-gateway "http://127.0.0.1:3717/api/mcp?project=<id>"Use 127.0.0.1 in the address, not localhost, because localhost can resolve to the IPv6 address, where the gateway does not listen. An agent's call waits while you decide on its action, up to 10 minutes by default, and your AI tool's own timeout for a tool call may end it sooner. Approvals and rules says how to raise that timeout in Claude Code.
Use ssh-gateway or the athenode daemon on one machine
ssh-gateway and the athenode daemon use the same data folder, ~/.athenode, and the same port, 3717, by default. One server holds a data folder at a time, so while one of them runs, the other does not start on that folder.
- Starting the athenode daemon while
ssh-gatewayruns fails with "is held by a running ssh-gateway daemon", followed by the address and process id.athenode service installrefuses in the same way. - Starting
ssh-gatewaywhile the athenode daemon runs fails with "is held by a running athenode daemon".
Both messages say what to do. Stop the one that runs with ssh-gateway stop or athenode stop and start the other. The athenode daemon contains the SSH gateway, so on a machine with both packages you need only the daemon. To run both at once, start ssh-gateway with its own data folder and port, for example ssh-gateway --data-dir ~/.ssh-gateway-data --port 3718. Each then has its own projects and connections.
Run ssh-gateway as a service
Run ssh-gateway as a service of your user when you want it to start by itself. Install the package globally first, because a service cannot run the npx cache. You write the service entry yourself.
On Linux, write the systemd user unit ~/.config/systemd/user/ssh-gateway.service:
[Unit]
Description=SSH Gateway (MCP server)
After=network-online.target
[Service]
ExecStart=/usr/bin/env ssh-gateway start
Restart=on-failure
# The ssh-agent socket, if connections use ssh-agent:
# Environment=SSH_AUTH_SOCK=%t/ssh-agent.socket
[Install]
WantedBy=default.targetThen load and start it:
systemctl --user daemon-reload && systemctl --user enable --now ssh-gatewayIf ssh-gateway is not on the service's path, put its absolute path in ExecStart, as command -v ssh-gateway prints it. systemctl --user stop ssh-gateway stops the gateway cleanly. A user service stops when you log out: run loginctl enable-linger $USER to keep it running without a login session.
On macOS, write the launchd agent ~/Library/LaunchAgents/com.athenode.plist, with the path that command -v ssh-gateway prints:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key><string>com.athenode</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/ssh-gateway</string>
<string>start</string>
</array>
<key>RunAtLoad</key><true/>
<key>KeepAlive</key><dict><key>SuccessfulExit</key><false/></dict>
<key>StandardErrorPath</key><string>/tmp/ssh-gateway.err.log</string>
</dict>
</plist>Then load it:
launchctl load ~/Library/LaunchAgents/com.athenode.plistlaunchd starts programs with a short path, so node must be found from it. If it is not, add an EnvironmentVariables entry with a PATH that contains the folder of node.
On Windows, run ssh-gateway in a terminal and stop it with Ctrl+C or ssh-gateway stop.
Check and stop ssh-gateway
| Command | What it does |
|---|---|
ssh-gateway status |
Says whether the gateway runs, with its address, process id and version. Exits with 0 when it runs and 3 when it does not. |
ssh-gateway stop |
Stops the running gateway. A gateway that has not stopped after 15 seconds is ended by force. |
ssh-gateway doctor |
Checks Node.js, the configuration, the data folder and its permissions, the master key, the port and ssh-agent. While the gateway is stopped it also checks the data and that the master key matches it. |
ssh-gateway help |
Lists the commands and options. Also -h and --help. |
ssh-gateway version |
Prints the version. Also -v and --version. |
Every command takes --data-dir <dir> to act on a gateway that keeps its data in another folder.
Configure ssh-gateway
Each setting is taken from the first source that sets it: a command-line option, then an environment variable, then config.json in the data folder, then the default. ssh-gateway reads no .env file.
ssh-gateway takes the same settings as the athenode daemon for its port, data folder, master key and public URL, the initial values of the SSH gateway settings, disablePoll, disableJobSync and disableHealthChecks. Service alert webhooks are a feature of Athenode Pro, so ssh-gateway sends browser notifications only. Configuration lists their names, variables, defaults and ranges. The settings for the plugin library and imports do not apply. A key it does not know is reported and ignored, and a value outside its range stops the start with a message that names the key.
The SSH gateway settings in config.json and the environment are initial values: the first start stores them, and from then on you change them on the Settings page of the web UI, without a restart, as history and gateway settings describes.
Connections that use ssh-agent use the agent that SSH_AUTH_SOCK names in the environment of the ssh-gateway process. A service started by systemd or launchd does not see the variables of your terminal, so set SSH_AUTH_SOCK in the service entry.
At start and every 24 hours, ssh-gateway asks npm for the latest version of the package, and the web UI shows a notice when a newer one is out. Set ATHENODE_NO_UPDATE_CHECK=1 to turn the check off.
Back up the data of ssh-gateway
The data folder holds the same files as the daemon's data folder: your projects and connections in db/, the master key in master.key, config.json, the lock of the running gateway, the error log in logs/athenode.log and the copies in backups/. The master key encrypts the stored passwords, keys and passphrases.
Warning
Without the original master key, the stored passwords, keys and passphrases cannot be decrypted. Back the key up separately from the data.
- Stop the gateway with
ssh-gateway stop, because the data of a running gateway cannot be copied safely. - Copy the
db/folder to your backup. - Copy
master.key, or the key you give withATHENODE_MASTER_KEYorATHENODE_MASTER_KEY_FILE, to another place than the copy ofdb/. - Start the gateway again.
Keep the copy of the data and the copy of the key apart, because together they give the stored secrets away. When the athenode package is installed too, athenode backup writes the whole data folder, key included, as one archive once ssh-gateway is stopped, as backup and restore describes.
When the gateway finds no key and its data holds secrets, it does not start and does not create a new key. Restore the key, or delete and enter again the secrets of your connections with a fresh data folder. A key that does not match the data is refused at start.
Update ssh-gateway
Install the new version globally, or run npx ssh-gateway again, which uses the newest version:
npm install -g ssh-gateway@latestRestart the gateway afterwards, with ssh-gateway stop and ssh-gateway start or through your service. When the new version converts your data, its first start saves a copy of the data in backups/ first. A version older than the one that converted the data refuses to start and names both versions: install the newer version again, or restore the copy from before the upgrade with the version that matches it. Backup and restore describes how to restore that copy.