Getting Started
What is NebulaSSH?
NebulaSSH is a web-based SSH terminal and remote management platform. It allows you to:
- Connect to remote servers via SSH directly from your browser
- Manage files using the built-in SFTP file manager
- Access remote desktops via VNC
- Remotely manage Windows PCs using the Connector Agent (like AnyDesk)
Quick Start
- Login — Use your credentials to sign in
- Add a Session — Click "New Session" in the SSH Terminal page and enter your server details
- Connect — Double-click a saved session to open an SSH terminal
SSH Terminal
Creating a Session
- Go to SSH Terminal from the sidebar
- Click New Session
- Fill in: Host, Port (default 22), Username, and Authentication method
- Choose Password or Private Key authentication
- Click Save
Using the Terminal
- Double-click a session to connect
- The terminal supports full ANSI colors and keyboard shortcuts
- Use Ctrl+Shift+V to paste into the terminal
- Right-click for copy/paste context menu
SFTP File Manager
Accessing Files
- When connected to an SSH session, click the SFTP tab in the sidebar
- Browse the remote file system with the file tree
File Operations
- Upload — Drag and drop files into the file manager
- Download — Click a file to download it
- Rename / Delete / Chmod — Right-click a file for context menu options
- Create Folder — Right-click in empty area and select "New Folder"
VNC Desktop
Opening VNC Desktop
- Connect to an SSH session in the Terminal
- Click the VNC Desktop button in the terminal toolbar
- A new browser tab opens with the remote desktop viewer
- The system automatically detects active displays and running browsers
Toolbar Controls
| Button | Description |
|---|---|
| Find Browser | Auto-detects browser windows (Chrome, Firefox, etc.) running on the remote desktop and connects to that display |
| Displays | Scan and switch between available X11 displays (:0, :1, etc.) |
| Ctrl+Alt+Del | Send the keyboard combination to the remote desktop |
| Toggle Scale/Fit | Switch between fit-to-window and 1:1 pixel rendering |
| Fullscreen | Toggle fullscreen view of the remote desktop |
| Refresh | Reconnect to VNC if the connection drops |
How It Works
Your Browser Remote Linux Server
| |
|--- Click "VNC Desktop" --------->|
| |
| 1. Detect X11 displays |
| 2. Find running browsers |
| 3. Start x11vnc server |
| 4. SSH port forwarding |
| |
|<-- WebSocket (noVNC) ------------|
| Live desktop stream |
| |
Display Auto-Detection
When you open VNC Desktop, the system automatically:
- Scans
/tmp/.X*-lockfiles for running X11 displays - Detects browser processes (Chrome, Firefox, Chromium, Electron, Playwright)
- Reads the
DISPLAYenvironment variable of each browser process - Connects to the display where a browser is actively running
- If no display exists, creates a new virtual display (Xvfb) at
1920x1080
AI Browser Viewing via VNC
How AI Views the Browser
When you click VNC Desktop on a terminal session, the remote server starts an x11vnc server on the display where a browser is running. The AI can then use the VNC API to view the desktop and take screenshots of what is visible.
Step-by-Step Flow
- Connect to SSH — Open an SSH terminal session to your remote server
- Click VNC Desktop — Click the VNC Desktop button in the terminal toolbar
- Browser is detected — The system auto-detects running browsers on the remote display
- AI gets visual access — Claude Code can now see the remote desktop through the VNC stream and take screenshots
- AI assists — Ask the AI to describe what it sees, click elements, navigate pages, or debug visual issues
Using the VNC API for AI
The AI can interact with VNC sessions through these API endpoints:
# Step 1: Start a VNC session (auto-detects browser display)
curl -s -X POST https://app.nebulassh.com/api/Vnc/start \
-H "Content-Type: application/json" \
-d '{"host":"your-server","port":22,"username":"user","password":"pass"}'
# Response:
# { "sessionId": "abc123", "display": ":1", "port": 5901 }
# Step 2: Auto-detect displays with running browsers
curl -s -X POST https://app.nebulassh.com/api/Vnc/auto-detect \
-H "Content-Type: application/json" \
-d '{"host":"your-server","port":22,"username":"user","password":"pass"}'
# Response:
# { "displays": [{ "display": ":1", "browsers": ["chrome"], "hasVnc": true }] }
# Step 3: Scan all available X11 displays
curl -s -X POST https://app.nebulassh.com/api/Vnc/scan \
-H "Content-Type: application/json" \
-d '{"host":"your-server","port":22,"username":"user","password":"pass"}'
# Step 4: Connect to a specific display
curl -s -X POST https://app.nebulassh.com/api/Vnc/connect \
-H "Content-Type: application/json" \
-d '{"host":"your-server","port":22,"username":"user","password":"pass","display":":1"}'
# Step 5: Check session status
curl -s https://app.nebulassh.com/api/Vnc/status/{sessionId}
# Step 6: Execute commands on the remote desktop session
curl -s -X POST https://app.nebulassh.com/api/Vnc/execute/{sessionId} \
-H "Content-Type: application/json" \
-d '{"command":"xdotool getactivewindow getwindowname"}'
# Step 7: Stop the session when done
curl -s -X POST https://app.nebulassh.com/api/Vnc/stop/{sessionId}
What the AI Can Do
| Capability | Description |
|---|---|
| View the desktop | See the remote graphical environment through the VNC stream via the browser tab |
| Detect browsers | Identify which browsers (Chrome, Firefox, etc.) are running and on which display |
| Take screenshots | Capture what is currently shown on the remote desktop for visual analysis |
| Execute commands | Run shell commands in the remote session (e.g., xdotool for mouse/keyboard automation) |
| Switch displays | Move between different X11 displays to find the right browser window |
Example: Ask AI to View a Browser
With Claude Code connected to your terminal, you can use natural language:
> Open VNC desktop and show me what's on the screen
> Take a screenshot of the remote browser
> What website is currently open in the browser on display :1?
> Click the login button on the remote browser
> Navigate the remote browser to https://example.com
Prerequisites
- An active SSH session to a Linux server with a graphical environment (X11)
- A browser running on the remote server (Chrome, Firefox, Chromium, etc.)
x11vncmust be installed on the remote server — runsudo apt install -y x11vncon the remote machine if VNC Desktop disconnects- Optional:
xdotoolfor mouse/keyboard automation via AI commands
Troubleshooting
x11vnc is not installed on the remote server. SSH into the remote machine and run:
sudo apt install -y x11vnc
sudo yum install -y x11vnc | For Arch: sudo pacman -S x11vnc
| Problem | Solution |
|---|---|
| VNC Desktop disconnects immediately on click | x11vnc is not installed on the remote server. Connect via SSH terminal and run: sudo apt install -y x11vnc. This is required on every remote machine you want to use VNC Desktop with. |
| No displays found | Ensure a graphical session (X11/Xvfb) is running. Start one with Xvfb :99 -screen 0 1920x1080x24 & |
| No browser detected | Launch a browser on the remote server: DISPLAY=:99 google-chrome & |
| VNC connection drops | Click Refresh in the VNC toolbar, or restart the session via the API |
x11vnc not found | Install it on the remote machine: sudo apt install -y x11vnc |
| Black/empty screen | Check the display number is correct. Use Displays dropdown to scan for active displays |
| AI cannot see the desktop | Ensure the VNC Desktop tab is open in your browser. The AI views the desktop through the browser-rendered VNC stream |
CDP Viewer
When to Use CDP Viewer vs VNC Desktop
| Feature | CDP Viewer | VNC Desktop |
|---|---|---|
| Requires GUI/X11 | No — works with headless Chrome | Yes — needs Xvfb or X11 |
| Requires x11vnc | No | Yes |
| View type | Live screencast (JPEG frames) + mouse/keyboard control | Live video stream of the whole desktop |
| Scope | The browser only | The entire remote desktop |
| Best for | Headless servers, web apps, AI/automation, CI/CD | Full desktop & native GUI apps |
| Dependencies | Just Google Chrome/Chromium (no extra packages) | x11vnc + Xvfb + browser |
How It Works
- Headless Chrome runs on the remote server with
--remote-debugging-port(CDP bound to127.0.0.1— it never needs to be exposed to the network). - When you click CDP Viewer, NebulaSSH uses your existing SSH connection to open a secure local tunnel to that debug port — so the credentials stay on the server side and the debug port is never public.
- The server connects to Chrome's CDP and issues
Page.startScreencast; Chrome streams live JPEG frames, which are relayed to your browser tab over a WebSocket and drawn on a canvas. - Your mouse and keyboard events are sent back as CDP
Input.dispatch*commands, so the view is fully interactive. A short-lived session token (not your password) is what the viewer tab uses.
Setup Guide: New Machine
Follow these steps to set up a fresh Linux server for the CDP Viewer:
Step 1: Install Google Chrome
# Ubuntu/Debian
wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | sudo gpg --dearmor -o /usr/share/keyrings/google-chrome.gpg
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/google-chrome.gpg] http://dl.google.com/linux/chrome/deb/ stable main" | sudo tee /etc/apt/sources.list.d/google-chrome.list
sudo apt update && sudo apt install -y google-chrome-stable
# CentOS/RHEL
sudo dnf install -y https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm
# Or install Chromium instead
sudo apt install -y chromium-browser # Ubuntu
sudo dnf install -y chromium # CentOS
Step 2: Start Headless Chrome with Remote Debugging
# Start Chrome headless with debugging port
google-chrome --headless=new --no-sandbox \
--remote-debugging-port=9222 \
--disable-gpu --disable-dev-shm-usage \
https://www.google.com &
# Verify it's running
curl -s http://127.0.0.1:9222/json/version
# Should return: { "Browser": "Chrome/...", ... }
# Check active pages
curl -s http://127.0.0.1:9222/json/list
# Should return a list of open tabs/pages
Step 3: Use the CDP Viewer
- Connect to the server via SSH in NebulaSSH
- Click the CDP Viewer button in the terminal action bar
- A new browser tab opens with the live view — it auto-detects the running Chrome and starts streaming
- Interact directly: click, type and scroll on the canvas. Use the toolbar to navigate to a URL, reload, change the viewport size, toggle auto-reload, or turn control off for view-only
AI/Automation Setup
For AI agents (like Claude Code) that need to view and interact with web pages on a remote server:
# One-liner: Start headless Chrome for AI viewing
google-chrome --headless=new --no-sandbox --remote-debugging-port=9222 \
--disable-gpu --disable-dev-shm-usage https://your-target-site.com &
# For Playwright/Node.js automation with viewing support
npx playwright install chromium
node -e "
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch({
headless: true,
args: ['--remote-debugging-port=9222', '--no-sandbox']
});
const page = await browser.newPage();
await page.goto('https://your-target-site.com');
// Browser stays open for the CDP Viewer to stream
console.log('Browser ready for viewing on port 9222');
})();
"
Keep Browser Running on Disconnect
To keep the headless browser running after you close the SSH session, use nohup or tmux:
# Method 1: nohup (simple)
nohup google-chrome --headless=new --no-sandbox --remote-debugging-port=9222 \
--disable-gpu --disable-dev-shm-usage https://your-site.com > /dev/null 2>&1 &
# Method 2: tmux (recommended for long-running)
tmux new-session -d -s chrome "google-chrome --headless=new --no-sandbox \
--remote-debugging-port=9222 --disable-gpu --disable-dev-shm-usage https://your-site.com"
# Method 3: systemd service (production)
sudo tee /etc/systemd/system/headless-chrome.service > /dev/null << 'EOF'
[Unit]
Description=Headless Chrome for CDP Viewer
After=network.target
[Service]
Type=simple
User=your-username
ExecStart=/usr/bin/google-chrome --headless=new --no-sandbox --remote-debugging-port=9222 --disable-gpu --disable-dev-shm-usage https://your-site.com
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now headless-chrome
Quick Checklist
| @# | Requirement | Check Command |
|---|---|---|
| 1 | Google Chrome or Chromium installed | google-chrome --version or chromium --version |
| 2 | Chrome running with debug port | pgrep -fa 'remote-debugging-port' |
| 3 | CDP responding (on the server) | curl -s http://127.0.0.1:9222/json/version |
| 4 | Pages available | curl -s http://127.0.0.1:9222/json/list |
Troubleshooting
| Problem | Solution |
|---|---|
| CDP Viewer says "No Chrome page/CDP target found" | Start Chrome with --remote-debugging-port=9222. Run: google-chrome --headless=new --no-sandbox --remote-debugging-port=9222 --disable-gpu --disable-dev-shm-usage https://google.com & |
| Chrome crashes immediately | Install missing dependencies: sudo apt install -y libnss3 libatk-bridge2.0-0 libx11-xcb1 libxcomposite1 libxdamage1 libxrandr2 libgbm1 libasound2 libpangocairo-1.0-0 libgtk-3-0. Also try adding --disable-dev-shm-usage flag. |
| "Address already in use" on port 9222 | Kill existing Chrome processes: pkill -9 -f chrome, then restart |
| Viewer tab shows "closed" / no frames | Verify CDP is accessible on the server: curl -s http://127.0.0.1:9222/json/list. If empty, no pages are open in the browser. Also confirm your SSH session to that host is still connected. |
| Snap Chromium doesn't work headless | Use Google Chrome instead: sudo apt install -y google-chrome-stable. Snap packages have sandbox restrictions that break headless mode. |
| View is blank/white | The page might still be loading. Use the toolbar to Navigate to a URL or hit Reload. |
NebulaSSH Connector Agent
- A new machine must enroll with a one-time enrollment token (Dashboard → Agents & Connector). Without it the agent is rejected.
- Listing or driving agents requires your API key (
X-Api-Keyheader), and you only ever see the agents you own.
Supported Platforms
| Platform | Agent | Shell Support | Requirements |
|---|---|---|---|
| Windows | NebulaSSHAgent.exe | PowerShell, CMD | No dependencies (self-contained .exe) |
| Linux | nebulassh-agent | Bash, Sh, Zsh | curl, python3 |
How It Works
The agent runs on the target machine and connects outbound to the relay server. No inbound ports need to be opened. The relay server acts as a bridge between you and the remote machine.
You (Browser / CLI) Relay Server Remote Machine
| (app.nebulassh.com) (Windows or Linux)
| | |
|--- Execute command --------->| |
| |--- Poll (every 2s) ---->|
| |<-- Here's a command ----|
| | |
| | [ Runs command ] |
| | |
| |<-- Result (stdout) -----|
|<-- Output -------------------| |
Key Features
- No IP required — connect using a unique Agent ID (e.g.,
031-975-703-405) - NAT/Firewall friendly — agent connects outbound via HTTPS, works behind any firewall
- Auto-reconnect — agent reconnects automatically if connection drops
- File transfer — upload and download files to/from the remote machine
- AI-friendly — all operations use simple REST APIs, perfect for Claude Code or any AI tool
Windows Agent Setup
https://app.nebulassh.com.
Step 1: Download the Agent
Download the right version for your Windows PC:
| Version | For | Size | Download |
|---|---|---|---|
NebulaSSHAgent.exe (x64) | Windows 10/11, Server 2016/2019/2022 (64-bit) | 68 MB | Download |
NebulaSSHAgent.exe (x86) | Windows 7/8/10/11, Server 2012 R2+ (32-bit) | 63 MB | Download |
NebulaSSHAgent.exe (ARM64) | Windows 11 ARM, Surface Pro X | 64 MB | Download |
No installation required. No .NET runtime needed. Single file, self-contained (bundles the .NET 8 runtime).
https://app.nebulassh.com.
| Version | For | Size | Download |
|---|---|---|---|
NebulaSSHAgent-Console.exe | Any Windows with .NET Framework 4.x (built into Win 8+, on Win 7 via update) | ~20 KB | Download |
NebulaSSHAgent-Console.exe --enroll-token NS-XXXXX-XXXXX-XXXXX-XXXXX — get the token from the dashboard (Agents & Connector). After enrollment just run NebulaSSHAgent-Console.exe. Point it elsewhere with --relay https://your-relay (defaults to app.nebulassh.com). Press Ctrl+C to stop.
Step 2: Get an Enrollment Token
A new machine must be authorized once before it can connect. Get a credential from the dashboard — open Agents & Connector or click the button (top-right of the Terminal). Use a one-time enrollment token (NS-…, valid 1 hour) or your permanent enrollment key (nse_…, reusable for every machine, never expires). You'll paste it into the agent in the next step. You provide it one of three ways:
- GUI agent: open Settings (⚙) and paste it into the Enrollment Token field before clicking Connect.
- Console agent:
set NEBULASSH_ENROLL_TOKEN=NS-...then run the exe, orNebulaSSHAgent-Console.exe --enroll-token NS-... - Any agent: drop the token into an
enroll.tokenfile next to the executable.
The token is single-use and only needed the first time. After the machine is enrolled it reconnects on its own — no token required.
Step 3: Run the Agent on Windows
Double-click NebulaSSHAgent.exe. The agent window opens and shows your unique NebulaSSH ID, the connection status, and a live activity log. Click Connect (or it connects automatically if Auto-Start is enabled in Settings).
┌─ NebulaSSH Agent ──────────────────────┐
│ Status: ● Connected │
│ Your NebulaSSH ID: 123-456-789-012 │
│ Relay: https://app.nebulassh.com │
│ │
│ [ Connect ] [ Disconnect ] [ ⚙ ] │
│ ─ Log ────────────────────────────── │
│ Registered as 123-456-789-012 │
│ Waiting for commands... │
└────────────────────────────────────────┘
Save this ID — you will use it to connect from the web UI or CLI. Closing the window minimizes the agent to the system tray so it keeps running in the background.
Settings
Open Settings (⚙) in the agent window to change:
| Setting | Description | Default |
|---|---|---|
| Relay URL | The relay server the agent connects to | https://app.nebulassh.com |
| Enrollment Token | One-time token to authorize this machine on first connect (cleared automatically once enrolled) | empty |
| Auto-Start | Launch minimized to the tray and connect on Windows login | Off |
Start Automatically on Login (Optional)
Enable Auto-Start in Settings — the agent registers itself to launch minimized to the tray and connect every time you sign in to Windows. To run it before login (as a service), use Task Scheduler:
- Open Task Scheduler > Create Basic Task
- Name:
NebulaSSH Agent - Trigger: When the computer starts
- Action: Start a program > Browse to
NebulaSSHAgent.exe, add the argument--minimized - Check: Run whether user is logged on or not
Linux Agent Setup
Requirements
curl— for HTTP communication with the relay serverpython3— for JSON parsingbash— shell (pre-installed on all Linux systems)
# Install dependencies (Ubuntu/Debian)
sudo apt update && sudo apt install -y curl python3
# For CentOS/RHEL/Fedora
sudo yum install -y curl python3
# For Arch Linux
sudo pacman -S curl python
Step 1: Download the Agent
Download the agent script and optional installer, or pull them straight to your machine with curl:
| File | For | Download |
|---|---|---|
nebulassh-agent | The Linux agent (bash). Run it directly. | Download |
install.sh | Guided installer (copies the agent + sets up a systemd service) | Download |
The agent defaults to the https://app.nebulassh.com relay — no extra flags needed.
Or download via curl (one-liner)
# Download the agent script straight to /usr/local/bin and make it executable
sudo curl -fsSL https://app.nebulassh.com/api/connector/download-linux-agent -o /usr/local/bin/nebulassh-agent
sudo chmod +x /usr/local/bin/nebulassh-agent
# (Optional) grab the guided installer instead
curl -fsSL https://app.nebulassh.com/api/connector/download-linux-installer -o install.sh
chmod +x install.sh && sudo ./install.sh
Step 2: Run the Agent
A brand-new machine must present an enrollment token on its first run so it binds to your account
— otherwise you’ll see “Enrollment required”. Get one from the dashboard: click the
button (top-right of the Terminal) or open Agents & Connector.
Use a one-time token (NS-…) or your permanent enrollment key (nse_…, reusable for every machine).
# First run — supply your token / key (one-time NS-… or permanent nse_…)
nebulassh-agent --relay https://app.nebulassh.com --enroll-token NS-XXXXX-XXXXX-XXXXX-XXXXX
# …or pass it via environment variable
NEBULASSH_ENROLL_TOKEN=NS-XXXXX-XXXXX-XXXXX-XXXXX nebulassh-agent --relay https://app.nebulassh.com
# After enrollment the token is no longer needed — just run:
nebulassh-agent --relay https://app.nebulassh.com
# Run as a background daemon
nebulassh-agent --relay https://app.nebulassh.com --daemon
On startup, the agent displays your unique Agent ID:
╔══════════════════════════════════════╗
║ NebulaSSH Linux Agent v1.0.0 ║
╚══════════════════════════════════════╝
Agent ID: 031-975-703-405
Relay: https://app.nebulassh.com
Shell: bash
Interval: 2s
[2026-05-31 17:43:05] [INFO] Registering agent 031-975-703-405...
[2026-05-31 17:43:05] [INFO] Registration successful!
[2026-05-31 17:43:05] [INFO] Machine: my-server
[2026-05-31 17:43:05] [INFO] OS: Linux 6.8.0-117-generic x86_64
[2026-05-31 17:43:05] [INFO] User: admin
Save the Agent ID — you’ll use it to connect from the web UI or CLI.
Agent Command-Line Options
| Flag | Description | Default |
|---|---|---|
--relay URL | Relay server URL (required on first run) | — |
--enroll-token TOKEN | One-time token (NS-…) or permanent key (nse_…); required on first enrollment only. Also via NEBULASSH_ENROLL_TOKEN. | — |
--shell SHELL | Default shell: bash, sh, zsh | bash |
--interval N | Poll interval in seconds | 2 |
--daemon | Run as background process | — |
--status | Show agent status (running/stopped) | — |
--stop | Stop the running agent | — |
--id | Print the Agent ID and exit | — |
Step 3: Auto-Start with Systemd (Recommended)
Set up the agent as a systemd service so it starts on boot. If the machine isn’t enrolled yet, include your
enrollment token below — a permanent key (nse_…) is best here since it never expires.
Already enrolled it in Step 2? Then just drop the NEBULASSH_ENROLL_TOKEN line.
# Create the service file
sudo tee /etc/systemd/system/nebulassh-agent.service <<EOF
[Unit]
Description=NebulaSSH Linux Agent
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=$USER
Environment=HOME=$HOME
# First-time enrollment only — use your permanent key (nse_…) or a one-time NS-… token.
# Safe to remove once the machine is enrolled.
Environment=NEBULASSH_ENROLL_TOKEN=nse_XXXXXXXXXXXXXXXXXXXXXXXX
ExecStart=/usr/local/bin/nebulassh-agent --relay https://app.nebulassh.com
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
# Enable and start the service
sudo systemctl daemon-reload
sudo systemctl enable nebulassh-agent
sudo systemctl start nebulassh-agent
Managing the Service
# Check status
sudo systemctl status nebulassh-agent
# View logs
sudo journalctl -u nebulassh-agent -f
# Restart agent
sudo systemctl restart nebulassh-agent
# Stop agent
sudo systemctl stop nebulassh-agent
Or Use the Installer Script
For a guided setup, use the included installer:
# Run the installer with your one-time enrollment token
# (generate it in Dashboard -> Agents & Connector)
sudo NEBULASSH_ENROLL_TOKEN=NS-XXXXX-XXXXX-XXXXX-XXXXX ./install.sh
# It will:
# 1. Copy the agent to /usr/local/bin/
# 2. Ask for your relay server URL
# 3. Enroll the machine under your account (consumes the token)
# 4. Optionally create a systemd service
Connecting to the Linux Agent
Once the agent is running, you can connect from anywhere:
From the Web UI:
- The agent appears in the Connector Agents list on the dashboard
- Click on the agent to send commands
From the CLI:
# List all online agents
nebulassh agents
# Execute a command on the Linux agent
nebulassh exec 031-975-703-405 "hostname && uptime && free -h"
# Interactive shell mode
nebulassh shell 031-975-703-405
From AI tools (Claude Code, etc.):
# Step 1: Send a command
curl -X POST https://app.nebulassh.com/api/connector/execute \
-H "Content-Type: application/json" \
-d '{"AgentId":"031-975-703-405","Command":"df -h && free -h","Shell":"bash"}'
# Response: {"commandId":"abc123def456"}
# Step 2: Get the result
curl https://app.nebulassh.com/api/connector/result/abc123def456
# Response: {"isCompleted":true,"output":"...","error":"","exitCode":0}
File Transfer
# Upload a file to the Linux agent
curl -X POST https://app.nebulassh.com/api/connector/upload \
-F "agentId=031-975-703-405" \
-F "remotePath=/home/user/deploy/app.tar.gz" \
-F "file=@./app.tar.gz"
# Download a file from the Linux agent
curl -X POST https://app.nebulassh.com/api/connector/download \
-H "Content-Type: application/json" \
-d '{"AgentId":"031-975-703-405","RemotePath":"/var/log/syslog"}'
How the Agent ID is Generated
The Agent ID is hardware-bound and stays the same across reboots:
- Generated from:
/etc/machine-id+hostname+username - Format:
XXX-XXX-XXX-XXX(12 digits) - Unique per machine + user combination
- No registration or account needed
Tunnel Connector Agent
Step 1: Download the Tunnel Agent
| Platform | For | Shell | Size | Download |
|---|---|---|---|---|
| Linux x64 | Any 64-bit Linux (glibc). Self-contained, no runtime. | real PTY (bash) | 35 MB | Download |
| Linux ARM64 | Raspberry Pi 64-bit, ARM servers. | real PTY (bash) | 33 MB | Download |
| Windows x64 | Windows 10/11, Server 2019/2022 (64-bit). GUI app — double-click, paste token, Connect. | ConPTY (full ANSI) | 69 MB | Download |
| Windows ARM64 | Windows 11 ARM, Surface Pro X. GUI app. | ConPTY (full ANSI) | 65 MB | Download |
| Windows 7/8 | Windows 7 SP1 / 8 (32- or 64-bit). Uses built-in .NET Framework 4.5+. GUI app. | command shell | 36 KB | Download |
| Android | Android 7.0+ (API 24). Universal APK — arm64, arm32 and x86_64 in one file. App — install, paste token, Connect. | real PTY (mksh) | 4.7 MB | Download |
The .NET 8 builds are single-file and self-contained (no runtime to install). The tiny Windows 7/8 build uses the .NET Framework that ships with Windows.
Step 2: Get an Enrollment Token
A new machine must be authorized once. Open Agents & Connector (or the button, top-right of the Terminal) and copy a one-time token (NS-…) or your permanent key (nse_…).
Step 3: Install on Linux
One-liner — download, enroll, and enable start-at-boot in one go (pick linux-x64 or linux-arm64):
# 64-bit x86 Linux
sudo curl -fsSL https://app.nebulassh.com/api/connector/download-tunnel-agent/linux-x64 \
-o /usr/local/bin/nebulassh-tunnel-agent
sudo chmod +x /usr/local/bin/nebulassh-tunnel-agent
# First run: enrolls the machine + installs a systemd service (auto-start on boot) + connects
sudo /usr/local/bin/nebulassh-tunnel-agent --enroll-token NS-XXXXX-XXXXX-XXXXX-XXXXX
# ARM64? swap the URL for .../download-tunnel-agent/linux-arm64
Or use the guided installer (downloads the right arch, enrolls, enables the service):
curl -fsSL https://app.nebulassh.com/api/connector/download-tunnel-agent/install-linux -o install-linux.sh
chmod +x install-linux.sh
sudo NEBULASSH_ENROLL_TOKEN=NS-XXXXX-XXXXX-XXXXX-XXXXX ./install-linux.sh
Run as root → a system-wide systemd service (nebulassh-tunnel-agent.service, starts at boot).
Run as a normal user → a per-user service (with linger enabled so it survives logout). The agent
writes the unit itself when you pass --install (the first run does this automatically).
Manual control
nebulassh-tunnel-agent --id # print this machine's Agent ID
nebulassh-tunnel-agent --run # run in the foreground
nebulassh-tunnel-agent --install # just enable start-at-boot and exit
nebulassh-tunnel-agent --uninstall # remove start-at-boot
nebulassh-tunnel-agent --no-autostart # run once without enabling boot start
# systemd (system service)
sudo systemctl status nebulassh-tunnel-agent
sudo journalctl -u nebulassh-tunnel-agent -f
sudo systemctl restart nebulassh-tunnel-agent
# systemd (per-user service)
systemctl --user status nebulassh-tunnel-agent
Step 3 (Windows): Install
Download the matching build above, then from PowerShell:
# Windows 10/11 (x64) — enrolls, enables start-at-logon (HKCU Run), and connects
.\nebulassh-tunnel-agent-win-x64.exe --enroll-token NS-XXXXX-XXXXX-XXXXX-XXXXX
# Windows 7/8 — same flags, uses the built-in .NET Framework
.\nebulassh-tunnel-agent-win7.exe --enroll-token NS-XXXXX-XXXXX-XXXXX-XXXXX
Prefer a script? Download install-windows.ps1
and run .\install-windows.ps1 -EnrollToken NS-… (add -Framework on Windows 7/8).
On Windows 10+/Linux you get a real PTY (full-screen apps work); on Windows 7/8 it's a plain command shell.
Step 4: Connect from the Web UI
- After enrolling, the machine appears under Agents & Connector and comes online.
- In the terminal, click New Session → the Connector tab.
- Type the Agent ID (e.g.
510-393-156-936) — or pick it from your agents list — and click Connect (or Save it as a reusable session). - You get an interactive shell — no host, no IP, no SSH credentials.
.exe, paste your enrollment token into the window, and click Connect.
It keeps running in the system tray and reconnects on every reboot. (The command-line flags below still
work for scripted installs.)
Connect an AI agent to the Tunnel Connector
Just like you can point an AI at a command agent, an AI assistant (or any script) can drive a
Tunnel Connector machine with nothing but its Agent ID and your
API key (nsk_… from Agents & Connector). One call runs a
command and returns its output — no interactive browser, no IP.
510-393-156-936
and my API key is nsk_….” — then it uses one of the methods below. Every call
is ownership-checked, so a key only ever reaches the agents it owns.
1. HTTP — works for any AI/tool that can call an API
# Run a command on the tunnel machine and get its output back
curl -s -X POST https://app.nebulassh.com/api/connector/tunnel-exec \
-H "X-Api-Key: nsk_XXXXXXXXXXXXXXXXXXXX" \
-H "Content-Type: application/json" \
-d '{"agentId":"510-393-156-936","command":"whoami && uname -a","shell":"auto"}'
# → {"output":"root\nLinux …\n","error":"","exitCode":0}
shell:auto(bash on Linux / PowerShell on Windows), orbash/cmd/powershell. Optional.timeoutSeconds: 1–300 (default 60). Auth viaX-Api-KeyorAuthorization: Bearer <key>.404= that agent has no live tunnel (not a Tunnel Connector agent, or offline). List yours:GET /api/connector/agents.
tunnel-exec is the recommended one-shot call — it blocks until the command finishes and returns the output in the same response.
POST /api/connector/execute → poll GET /api/connector/result/{commandId}
pattern (the one most AI agents reach for by default) also works on Tunnel Connector agents:
the server detects the live tunnel, runs the command over it, and fills in the result so your poll
completes normally. Either style is fine — but for a one-shot command, tunnel-exec saves you the polling loop.
# Classic queue + poll (works for tunnel AND classic agents)
CID=$(curl -s -X POST https://app.nebulassh.com/api/connector/execute \
-H "X-Api-Key: nsk_XXXX" -H "Content-Type: application/json" \
-d '{"agentId":"510-393-156-936","command":"uname -a","shell":"bash"}' | jq -r .commandId)
curl -s https://app.nebulassh.com/api/connector/result/$CID -H "X-Api-Key: nsk_XXXX"
# → {"isCompleted":true,"output":"Linux …\n","error":"","exitCode":0}
If result stays {"isCompleted":false} forever, the agent on that box is an
older tunnel build without command support — re-download the latest agent (above) and it will respond.
2. The nebulassh CLI
curl -fsSL https://app.nebulassh.com/api/connector/cli -o nebulassh && chmod +x nebulassh
export NEBULASSH_API_KEY=nsk_XXXXXXXXXXXXXXXXXXXX
./nebulassh agents # list your agents
./nebulassh exec 510-393-156-936 "df -h && free -h" # works for tunnel AND classic agents
./nebulassh tunnel-exec 510-393-156-936 "uptime" # force the tunnel path
exec tries the tunnel first and falls back to the classic command connector, so the same command works for either agent kind.
3. MCP server (Claude and other MCP clients)
The bundled MCP server (connector/nebulassh-mcp) exposes tools an assistant can call directly:
list_agents, run_command (works for both agent kinds), tunnel_exec,
generate_enrollment_token, and more. Point your MCP client at the server's /mcp
endpoint and send the key as Authorization: Bearer nsk_…; then just ask it to run a command
on the agent ID.
exec support —
re-download if you installed a very early one). Also, the agent and the API you call must be the
same NebulaSSH server (tunnels are held in memory per instance): if the agent’s
--relay is app.nebulassh.com, call app.nebulassh.com.
Linux Setup (Fresh PC)
curl and python3 are required.
Step 1: Install Dependencies
Run these commands on a fresh Ubuntu/Debian Linux machine:
# Update package list
sudo apt update
# Install curl (HTTP client for API communication)
sudo apt install -y curl
# Install python3 (JSON parsing for CLI tool)
sudo apt install -y python3
# Optional: Install ca-certificates (for HTTPS/SSL)
sudo apt install -y ca-certificates
For CentOS/RHEL/Fedora:
sudo yum install -y curl python3 ca-certificates
For Arch Linux:
sudo pacman -S curl python ca-certificates
Step 2: Verify Installation
curl --version # Must show version 7.x or 8.x
python3 --version # Must show 3.6+
Step 3: Set Relay Server
# Set relay URL (add to ~/.bashrc for persistence)
export NEBULASSH_RELAY=https://app.nebulassh.com
# Make permanent
echo 'export NEBULASSH_RELAY=https://app.nebulassh.com' >> ~/.bashrc
source ~/.bashrc
Step 4: Test Connectivity
# Verify the relay is reachable (public endpoint — no key needed)
curl -sI https://app.nebulassh.com/api/connector/cli | head -1
# Should show: HTTP/2 200
# Listing agents now requires your API key (X-Api-Key); without it you get 401
curl -s -H "X-Api-Key: $NEBULASSH_API_KEY" https://app.nebulassh.com/api/connector/agents
# Returns your agents as JSON ([] if none). Get the key from Dashboard -> Agents & Connector.
One-Shot Setup (Copy-Paste)
Run this single block on a brand new Linux machine to install everything:
sudo apt update && sudo apt install -y curl python3 ca-certificates && \
echo 'export NEBULASSH_RELAY=https://app.nebulassh.com' >> ~/.bashrc && \
export NEBULASSH_RELAY=https://app.nebulassh.com && \
echo "Setup complete! Now install the CLI tool (see next section)"
CLI Tool (Copy & Install)
X-Api-Key) for control-plane calls — set NEBULASSH_API_KEY before running nebulassh agents/exec.
Install Method: Copy Script
Create the file and paste the script:
#!/bin/bash
#
# NebulaSSH CLI Client
# Remote command execution on Windows PCs via relay server
#
# Usage:
# nebulassh agents - List connected agents
# nebulassh exec 123-456-789 "hostname" - Run via PowerShell (default)
# nebulassh exec 123-456-789 "dir C:\" cmd - Run via CMD
# nebulassh shell 123-456-789 - Interactive shell mode
#
RELAY_URL="${NEBULASSH_RELAY:-https://app.nebulassh.com}"
API_KEY="${NEBULASSH_API_KEY:-}"
POLL_INTERVAL=1
MAX_WAIT=120
require_key() {
if [ -z "$API_KEY" ]; then
echo -e "${RED}[ERROR]${NC} Set NEBULASSH_API_KEY first."
echo "Get your key from the NebulaSSH dashboard -> Agents & Connector."
exit 1
fi
}
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
BOLD='\033[1m'
NC='\033[0m'
print_banner() {
echo -e "${CYAN}"
echo ' _ _ _ _ ____ ____ _ _ '
echo '| \ | | ___| |__ _ _| | __ _ / ___/ ___|| | | |'
echo '| \| |/ _ \ '\''_ \| | | | |/ _` |\___ \___ \| |_| |'
echo '| |\ | __/ |_) | |_| | | (_| | ___) |__) | _ |'
echo '|_| \_|\___|_.__/ \__,_|_|\__,_||____/____/|_| |_|'
echo -e "${NC}"
echo -e " ${BOLD}NebulaSSH CLI Client${NC} - Remote Windows Command Execution"
echo ""
}
cmd_agents() {
require_key
echo -e "${BOLD}Your Agents:${NC}"
echo ""
response=$(curl -s -H "X-Api-Key: ${API_KEY}" "${RELAY_URL}/api/connector/agents")
if [ -z "$response" ] || [ "$response" = "[]" ]; then
echo -e " ${YELLOW}No agents connected.${NC}"
return
fi
echo -e " ${BOLD}ID Machine OS User Status${NC}"
echo " ────────────────────────────────────────────────────────────────────────────────────────────"
echo "$response" | python3 -c "
import sys, json
agents = json.load(sys.stdin)
for a in agents:
aid = a.get('agentId', 'N/A')
machine = a.get('machineName', 'N/A')[:16]
os_ver = a.get('osVersion', 'N/A')[:28]
user = a.get('username', 'N/A')[:10]
online = 'Online' if a.get('isOnline') else 'Offline'
print(f' {aid:<20}{machine:<17}{os_ver:<30}{user:<12}{online}')
" 2>/dev/null
echo ""
}
cmd_exec() {
local agent_id="$1"
local command="$2"
local shell="${3:-powershell}"
if [ -z "$agent_id" ] || [ -z "$command" ]; then
echo -e "${RED}Usage: nebulassh exec <agent-id> <command> [powershell|cmd]${NC}"
return 1
fi
require_key
echo -e "${CYAN}[NebulaSSH]${NC} Sending to ${BOLD}${agent_id}${NC} (${shell}): ${command}"
echo ""
response=$(curl -s -X POST "${RELAY_URL}/api/connector/execute" \
-H "Content-Type: application/json" \
-H "X-Api-Key: ${API_KEY}" \
-d "{\"agentId\":\"${agent_id}\",\"command\":\"${command}\",\"shell\":\"${shell}\"}")
command_id=$(echo "$response" | python3 -c "import sys,json; print(json.load(sys.stdin).get('commandId',''))" 2>/dev/null)
if [ -z "$command_id" ]; then
error=$(echo "$response" | python3 -c "import sys,json; print(json.load(sys.stdin).get('error','Unknown error'))" 2>/dev/null)
echo -e "${RED}[ERROR]${NC} ${error}"
return 1
fi
waited=0
while [ $waited -lt $MAX_WAIT ]; do
result=$(curl -s -H "X-Api-Key: ${API_KEY}" "${RELAY_URL}/api/connector/result/${command_id}")
is_completed=$(echo "$result" | python3 -c "import sys,json; print(json.load(sys.stdin).get('isCompleted', False))" 2>/dev/null)
if [ "$is_completed" = "True" ] || [ "$is_completed" = "true" ]; then
echo "$result" | python3 -c "
import sys, json
r = json.load(sys.stdin)
output = r.get('output', '')
error = r.get('error', '')
exit_code = r.get('exitCode', -1)
if output:
print(output.rstrip())
if error:
print('\033[0;31m' + error.rstrip() + '\033[0m')
print(f'\n\033[0;36m[NebulaSSH]\033[0m Exit code: {exit_code}')
" 2>/dev/null
return 0
fi
sleep $POLL_INTERVAL
waited=$((waited + POLL_INTERVAL))
printf "\r Waiting... %ds" "$waited"
done
echo ""
echo -e "${RED}[TIMEOUT]${NC} Command did not complete within ${MAX_WAIT}s"
return 1
}
cmd_shell() {
local agent_id="$1"
if [ -z "$agent_id" ]; then
echo -e "${RED}Usage: nebulassh shell <agent-id>${NC}"
return 1
fi
echo -e "${GREEN}[NebulaSSH Shell]${NC} Connected to ${BOLD}${agent_id}${NC}"
echo -e "Prefix with ${BOLD}cmd:${NC} to use CMD. Type ${BOLD}exit${NC} to quit."
echo ""
while true; do
echo -ne "${GREEN}nebulassh@${agent_id}${NC}> "
read -r input
[ -z "$input" ] && continue
[ "$input" = "exit" ] || [ "$input" = "quit" ] && { echo -e "${CYAN}[NebulaSSH]${NC} Disconnected."; break; }
shell="powershell"
command="$input"
if [[ "$input" == cmd:* ]]; then
shell="cmd"
command="${input#cmd:}"
command="$(echo "$command" | sed 's/^ *//')"
fi
cmd_exec "$agent_id" "$command" "$shell"
echo ""
done
}
case "${1}" in
agents|list|ls)
print_banner
cmd_agents
;;
exec|run|e)
cmd_exec "$2" "$3" "$4"
;;
shell|sh|connect)
print_banner
cmd_shell "$2"
;;
--relay|-r)
RELAY_URL="$2"
shift 2
exec "$0" "$@"
;;
--help|-h|help|"")
print_banner
echo "Usage: nebulassh <command> [options]"
echo ""
echo "Commands:"
echo " agents List all connected Windows agents"
echo " exec <agent-id> <command> [shell] Execute a command (shell: powershell|cmd)"
echo " shell <agent-id> Interactive shell session"
echo ""
echo "Agent ID format: XXX-XXX-XXX (hardware-bound, persistent)"
echo ""
echo "Environment:"
echo " NEBULASSH_API_KEY Your control-plane API key (required). Dashboard -> Agents & Connector."
echo " NEBULASSH_RELAY Relay server URL (default: https://app.nebulassh.com)"
echo ""
echo "Examples:"
echo " nebulassh agents"
echo " nebulassh exec 123-456-789 \"Get-Process\" powershell"
echo " nebulassh exec 123-456-789 \"ipconfig /all\" cmd"
echo " nebulassh shell 123-456-789"
echo ""
;;
*)
echo -e "${RED}Unknown command: $1${NC}"
echo "Run 'nebulassh --help' for usage."
exit 1
;;
esac
Quick Install Commands
After copying the script above, run:
# Save the script (paste copied content)
sudo nano /usr/local/bin/nebulassh
# (Paste the script, then Ctrl+X, Y, Enter)
# Make executable
sudo chmod +x /usr/local/bin/nebulassh
# Verify installation
nebulassh --help
One-Liner Install (Download from Server)
# Download and install CLI tool directly
curl -s https://app.nebulassh.com/api/connector/cli -o /usr/local/bin/nebulassh && \
chmod +x /usr/local/bin/nebulassh && \
nebulassh --help
For AI / Claude Code
If Claude Code (CLI AI) is installed on the Linux machine, it can use the tool directly or call the API with curl. It needs curl, the relay URL, and your API key (NEBULASSH_API_KEY / X-Api-Key).
# AI can use the CLI tool (export NEBULASSH_API_KEY first)
nebulassh exec 123-456-789-012 "Get-Process"
# Or call API directly with curl (the API key is required)
curl -s -X POST https://app.nebulassh.com/api/connector/execute \
-H "Content-Type: application/json" \
-H "X-Api-Key: $NEBULASSH_API_KEY" \
-d '{"agentId":"123-456-789-012","command":"hostname","shell":"powershell"}'
Connect from Linux
Option A: CLI Client
# Set relay server
export NEBULASSH_RELAY=https://app.nebulassh.com
# Set your API key (required) - get it from Dashboard -> Agents & Connector
export NEBULASSH_API_KEY=nsk_your_key_here
# List the agents you own
./nebulassh agents
# Execute a command (PowerShell - default)
./nebulassh exec 123-456-789-012 "Get-Process | Select -First 5"
# Execute a command (CMD)
./nebulassh exec 123-456-789-012 "ipconfig /all" cmd
# Interactive shell session
./nebulassh shell 123-456-789-012
Option B: Claude CLI (AI)
If you have Claude CLI installed, simply tell it in natural language:
> Connect to NebulaSSH agent 123-456-789-012 and show me running services
> Check disk space on agent 456-789-012-345
> Run "Get-EventLog -LogName System -Newest 10" on agent 123-456-789-012
Option C: Direct API (curl)
# All control-plane calls require your API key (X-Api-Key header).
KEY="nsk_your_key_here"
# List the agents you own
curl -s -H "X-Api-Key: $KEY" https://app.nebulassh.com/api/connector/agents
# Send a command
curl -s -X POST https://app.nebulassh.com/api/connector/execute \
-H "Content-Type: application/json" \
-H "X-Api-Key: $KEY" \
-d '{"agentId":"123-456-789-012","command":"hostname","shell":"powershell"}'
# Returns: {"commandId":"abc123def456"}
# Get result (poll until isCompleted is true)
curl -s -H "X-Api-Key: $KEY" https://app.nebulassh.com/api/connector/result/abc123def456
# Returns: {"isCompleted":true,"output":"DESKTOP-PC\n","error":"","exitCode":0}
Common Commands
System Information
nebulassh exec ID "systeminfo"
nebulassh exec ID "hostname"
nebulassh exec ID "whoami"
nebulassh exec ID "[System.Environment]::OSVersion"
Network
nebulassh exec ID "ipconfig /all" cmd
nebulassh exec ID "netstat -an" cmd
nebulassh exec ID "Get-NetIPAddress | Format-Table"
nebulassh exec ID "Test-NetConnection google.com -Port 443"
Processes & Services
nebulassh exec ID "Get-Process | Sort-Object CPU -Descending | Select -First 10"
nebulassh exec ID "Get-Service | Where-Object Status -eq Running"
nebulassh exec ID "tasklist" cmd
Disk & Files
nebulassh exec ID "Get-PSDrive -PSProvider FileSystem"
nebulassh exec ID "dir C:\" cmd
nebulassh exec ID "Get-ChildItem C:\Users -Recurse -Depth 1"
Event Logs
nebulassh exec ID "Get-EventLog -LogName System -Newest 5"
nebulassh exec ID "Get-EventLog -LogName Application -EntryType Error -Newest 10"
Installed Software
nebulassh exec ID "Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* | Select DisplayName, DisplayVersion | Sort DisplayName"
File Transfer via Agent
How It Works
Upload (You → Agent):
1. You upload file to app.nebulassh.com with agent ID + destination path
2. Agent picks up the file during its next poll cycle
3. Agent saves the file to the specified path
Download (Agent → You):
1. You request a file from the agent with the remote file path
2. Agent reads the file and uploads it to app.nebulassh.com
3. You download the file from app.nebulassh.com
Upload a File to Agent
# Upload a file to the agent's C:\Temp folder
curl -X POST https://app.nebulassh.com/api/connector/upload \
-F "agentId=123-456-789-012" \
-F "remotePath=C:\Temp\myfile.zip" \
-F "file=@/path/to/local/myfile.zip"
# Response:
# { "transferId": "abc123def456", "fileName": "myfile.zip", "fileSize": 1048576 }
Download a File from Agent
# Step 1: Request the file
curl -X POST https://app.nebulassh.com/api/connector/download \
-H "Content-Type: application/json" \
-d '{"agentId":"123-456-789-012","remotePath":"C:\\Users\\admin\\Documents\\report.pdf"}'
# Response:
# { "transferId": "xyz789abc012" }
# Step 2: Poll until ready
curl -s https://app.nebulassh.com/api/connector/transfer/xyz789abc012/status
# { "isCompleted": true, "isError": false, "fileName": "report.pdf", "fileSize": 204800 }
# Step 3: Download the file
curl -o report.pdf https://app.nebulassh.com/api/connector/transfer/xyz789abc012/content
Check Transfer Status
curl -s https://app.nebulassh.com/api/connector/transfer/{transferId}/status
# Response:
{
"transferId": "abc123def456",
"type": "upload", // "upload" = sending to agent, "download" = getting from agent
"isCompleted": true,
"isError": false,
"errorMessage": null,
"fileName": "myfile.zip",
"fileSize": 1048576
}
Notes
- The agent polls every 2 seconds, so transfers start almost immediately
- Maximum file size depends on available server memory (files are held in memory during transfer)
- The agent must be online when you initiate the transfer
- Directories on the agent are created automatically if they don't exist
- For folder transfers, zip the folder first, upload, then extract via a command
API Reference
Base URL: https://app.nebulassh.com/api/connector
Agent-Side Endpoints
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| POST | /register | Register agent, get unique ID | X-Enroll-Token (new agents) |
| POST | /heartbeat | Heartbeat + fetch pending commands | Headers |
| POST | /result | Submit command output | Headers |
| POST | /disconnect | Unregister agent | Headers |
Auth Headers: X-Agent-Id and X-Agent-Token (received during registration)
Client-Side Endpoints
All client-side endpoints require your API key via the X-Api-Key header (or Authorization: Bearer), or a logged-in session cookie. Each call is scoped to the agents you own.
| Method | Endpoint | Description |
|---|---|---|
| GET | /agents | List the agents you own |
| POST | /execute | Send command to an agent you own |
| GET | /result/{commandId} | Get command result |
| POST | /enroll-token | Generate a one-time enrollment token |
| POST | /agents/{agentId}/release | Release (un-claim) an agent you own |
| GET | /api-key | Get your current API key |
| POST | /api-key/regenerate | Regenerate your API key |
File Transfer Endpoints
| Method | Endpoint | Description |
|---|---|---|
| POST | /upload | Upload file to send to agent (multipart form) |
| POST | /download | Request file download from agent |
| GET | /transfer/{id}/status | Check transfer status |
| GET | /transfer/{id}/content | Download completed file |
File Transfer (Agent-Side)
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| GET | /transfers | Get pending file transfers | Headers |
| GET | /transfer/{id}/data | Download file data (for upload transfers) | Headers |
| POST | /transfer/{id}/data | Upload file data (for download transfers) | Headers |
| POST | /transfer/{id}/complete | Report transfer success/failure | Headers |
Example: Execute Command
# POST /api/connector/execute
# Header: X-Api-Key: nsk_your_key_here
{
"agentId": "123-456-789-012",
"command": "Get-Process",
"shell": "powershell"
}
# Response
{ "commandId": "abc123def456" }
# GET /api/connector/result/abc123def456
{
"isCompleted": true,
"output": "Handles NPM(K) PM(K) ...",
"error": "",
"exitCode": 0
}
MCP Server (AI Agents)
Tools exposed
| Tool | What it does |
|---|---|
list_agents | List the agents you own and their online status |
run_command | Run a command on an agent and wait for the output |
execute_command | Queue a command and return a commandId immediately |
get_command_result | Fetch the result of a queued command |
generate_enrollment_token | Mint a one-time token to enroll a new agent |
release_agent | Remove an agent from your account |
Connect from Claude Code
Add it as an HTTP MCP server, passing your API key as a bearer header:
# .mcp.json (or: claude mcp add)
{
"mcpServers": {
"nebulassh": {
"type": "http",
"url": "https://mcp.nebulassh.com/mcp",
"headers": { "Authorization": "Bearer nsk_your_api_key_here" }
}
}
}
Then just ask in natural language, e.g. "list my NebulaSSH agents" or "run Get-Process on agent 123-456-789-012".
Self-host the MCP server
The server ships in connector/nebulassh-mcp/ (Node.js 18+). Build and run it, then put it behind TLS:
cd connector/nebulassh-mcp
npm install && npm run build
cp .env.example .env # set NEBULASSH_RELAY / PORT
npm start # POST http://localhost:8848/mcp
It's stateless and multi-user: each request carries its own API key, so one instance can serve everyone. See the project README for the systemd unit and nginx config.
Troubleshooting
| Problem | Solution |
|---|---|
Agent shows [FATAL] HTTP Error | Check internet connection. Verify the relay URL is reachable. |
Agent shows Could not connect | Ensure https://app.nebulassh.com is accessible from the Windows PC. Check firewall. |
| Agent not in agents list | Agent may have timed out (5 min idle). Restart the agent. |
| Command times out | Commands have a 120-second timeout. Break long operations into smaller steps. |
Agent not found or offline | Wrong agent ID or agent not running. Check with nebulassh agents. |
| SSL/Certificate errors | Ensure system date/time is correct on the Windows PC. |
| File transfer stuck / not completing | Agent must be online. Check transfer status with /transfer/{id}/status. Ensure agent has write permissions to the target path. |
| Upload fails with "Agent not found" | Agent is offline or wrong ID. Verify with /agents endpoint. |
| SSH terminal not connecting | Verify host, port, and credentials. Ensure SSH is enabled on the remote server. |
| VNC Desktop disconnects immediately | x11vnc is not installed on the remote server. SSH into the remote machine and run: sudo apt install -y x11vnc |