Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 102 additions & 69 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,7 @@

<h1 align="center">maglev</h1>

<p align="center">Run AI coding shells locally, then control them from your browser or phone.</p>

## Start Here

Pick the setup that matches where your shell will run:

| Use case | Best fit | Open the UI from |
|----------|----------|------------------|
| Local laptop or devbox | Hub and shell on the same machine | `http://localhost:3006` |
| SSH workstation | Hub on the remote machine, browser through an SSH tunnel | `http://localhost:3006` on your laptop |
| Slurm/HPC node | Hub inside the allocation, broker on a reachable login/VNC node | The URL printed by `maglev hub start --remote` |
<p align="center">Run AI coding sessions locally, then control them from your browser or phone.</p>

## Install

Expand Down Expand Up @@ -43,130 +33,173 @@ If `maglev` is not found after install:
export PATH="$HOME/.local/bin:$PATH"
```

## Build From Source
## Main Features

Use this path when working from a checkout, testing unreleased changes, or using a platform without a release artifact.
- Persistent session list that survives browser disconnects and Maglev backend restarts.
- Auto-connect browser sessions back to running terminals when the page or backend reconnects.
- Auto-respawn terminals when the Maglev backend restarts, with optional per-terminal startup commands.
- Code diff and review views for inspecting changes and leaving comments.
- File browser support for each session, including sessions running inside worktrees.
- Worktree support for creating isolated coding sessions from the web UI.

Prerequisites:
## User Workflows

- `git`
- `bun`
- Optional but recommended: `rg` and `difft`
Pick the setup that matches where your sessions will run:

```bash
git clone https://github.com/bmarimuthu-nv/Maglev.git maglev
cd maglev
./install.sh
maglev --version
```
| Use case | Best fit | Open the UI from |
|----------|----------|------------------|
| Local laptop or devbox | Hub and sessions on the same machine | The URL printed by `maglev hub start` |
| SSH workstation | Hub on the remote machine, browser through an SSH tunnel | The forwarded URL on your laptop |
| Slurm/HPC node | Server on a reachable login/VNC node, hub inside the allocation | The URL printed by `maglev hub start --remote` |

`./install.sh` builds the standalone binary from source and installs `maglev` to `$HOME/.local/bin`.
### Direct vs Server Mode

Common variants:
Maglev does not guess whether your browser can reach a hub. You choose the mode when starting the hub.

Use direct mode when your browser can reach the hub URL directly, including through an SSH tunnel:

```bash
# Install somewhere else
MAGLEV_INSTALL_DIR="$HOME/bin" ./install.sh
maglev hub start --name devbox
```

# Force dependency reinstall before building
FORCE=1 ./install.sh
Use server mode when the hub runs somewhere your browser cannot reach directly, such as inside a Slurm allocation or container:

# Build only, without installing
bun install
bun run build:standalone
```bash
maglev hub start --name "slurm-${SLURM_JOB_ID:-manual}" --remote
```

## Local Setup
In server mode, the hub still listens on a local port, but it also registers with `maglev server`. The server URL is discovered from `~/.maglev/server-url` written by `maglev server`, from saved settings, or from `--server-url <url>`.

### Local Setup

Use this when your browser and coding shell are on the same machine.
Use this direct-mode setup when your browser and coding environment are on the same machine.

```bash
maglev hub start --name local
maglev shell
```

Open:

```text
http://localhost:3006
The URL printed by `maglev hub start`.
```

Optional: start the runner if you want the web UI to create new shells later.
By default, `maglev hub start` chooses a free local port to avoid conflicts. If you want the traditional fixed local URL, pin the port explicitly:

```bash
maglev runner start
maglev hub start --name local --port 3006
```

## SSH Setup
Then open `http://localhost:3006`.

Create sessions from the web UI. `maglev hub start` also starts the local runner for that hub, so you do not need to run `maglev shell` or `maglev runner start` manually for the normal flow.

### SSH Setup

Use this when Maglev runs on a remote workstation but you browse from your laptop.
Use this direct-mode setup when Maglev runs on a remote workstation and your browser reaches it through an SSH tunnel.

On the remote workstation:

```bash
maglev hub start --name devbox --host 127.0.0.1
maglev shell
```

By default, the remote hub also chooses a free local port to avoid conflicts. Use the port printed by `maglev hub start` in your SSH tunnel. Example, if the hub prints `http://127.0.0.1:43891`:

On your laptop:

```bash
ssh -L 3006:127.0.0.1:3006 user@devbox
ssh -L 43891:127.0.0.1:43891 user@devbox
```

Then open this on your laptop:

```text
http://localhost:3006
http://localhost:43891
```

If you prefer a stable tunnel command, pin the remote hub port:

```bash
maglev hub start --name devbox --host 127.0.0.1 --port 3006
```

Optional: keep remote spawning available from the web UI:
Then forward `3006`:

```bash
maglev runner start
ssh -L 3006:127.0.0.1:3006 user@devbox
```

## Slurm / HPC Setup
Create sessions from the web UI after the runner appears in the machine list.

Use this when shells run on ephemeral compute nodes that your browser cannot reach directly.
### Slurm / HPC Setup

On a stable login, VNC, or jump node:
Use this server-mode setup when sessions run on ephemeral compute nodes that your browser cannot reach directly.

```bash
# Terminal 1: keep the broker running
maglev server
Assumption: the login node and allocated Slurm node/container share the same home directory. At minimum, they must share `~/.maglev`. The server writes connection and auth state there, and `maglev hub start --remote` reads it inside the allocation. If your site gives jobs a different home directory, mount or bind the login node's `~/.maglev` into the job/container before starting the hub.

| Where | Run | Purpose |
|-------|-----|---------|
| Client laptop/browser | Open the URL printed by `maglev hub start --remote` | Use the web UI |
| Login, VNC, or jump node | `maglev server service install` | Keep the Maglev server reachable |
| Login, VNC, or jump node | `maglev auth github login` | Authenticate browser access once |
| Slurm node/container | `maglev hub start --name "slurm-${SLURM_JOB_ID:-manual}" --remote` | Start the hub and runner inside the allocation |

# Terminal 2: authenticate browser access once
The default Linux setup is to run the server as a user service on the stable login/VNC/jump node:

```bash
maglev server service install
maglev auth github login
```

Inside the Slurm allocation:
Then, inside the Slurm allocation:

```bash
srun --pty bash
maglev hub start --name "slurm-${SLURM_JOB_ID:-manual}" --remote
maglev shell
```

Open the URL printed by `maglev hub start --remote`.

If the browser cannot reach the broker hostname, start the broker with the public URL you actually use:
If the browser cannot reach the server hostname, start the server with the public URL you actually use:

```bash
maglev server --public-url https://your-reachable-broker.example
maglev server --public-url https://your-reachable-server.example
```

If the login node and compute node do not share `~/.maglev`, pass broker details explicitly:
If Linux user services are not available on the login/VNC/jump node, keep `maglev server` running in a terminal or under your site's preferred process manager.

## Build From Source

Use this path when working from a checkout, testing unreleased changes, or using a platform without a release artifact.

Prerequisites:

- `git`
- `bun`
- Optional but recommended: `rg` and `difft`

```bash
# On the login/VNC node
cat ~/.maglev/broker-url
cat ~/.maglev/broker-key
git clone https://github.com/bmarimuthu-nv/Maglev.git maglev
cd maglev
./install.sh
maglev --version
```

`./install.sh` builds the standalone binary from source and installs `maglev` to `$HOME/.local/bin`.

# Inside the Slurm allocation
maglev hub start --remote \
--broker-url http://login-node:3010 \
--broker-token "<broker-key-from-login-node>"
Common variants:

```bash
# Install somewhere else
MAGLEV_INSTALL_DIR="$HOME/bin" ./install.sh

# Force dependency reinstall before building
FORCE=1 ./install.sh

# Build only, without installing
bun install
bun run build:standalone
```

## Daily Commands
Expand All @@ -185,11 +218,11 @@ maglev server hubs

## Services

For long-running Linux user services:
For long-running Linux hosts, install services instead of keeping foreground terminals open:

```bash
maglev hub service install
maglev server service install
maglev hub service install
```

For named hubs:
Expand All @@ -203,9 +236,9 @@ maglev hub logs --name devbox-a --follow
## Mental Model

- `maglev hub` stores session state and serves the web UI.
- `maglev shell` starts a shell session and registers it with the hub.
- `maglev runner` lets the web UI spawn new shells on that machine.
- `maglev server` is the optional broker for machines your browser cannot reach directly.
- `maglev hub start` starts the matching runner automatically.
- `maglev runner` lets the web UI create sessions on that machine; direct runner commands are mostly for status, logs, and troubleshooting.
- `maglev server` is the remote access entrypoint for machines your browser cannot reach directly.

## More Docs

Expand Down
16 changes: 8 additions & 8 deletions cli/src/commands/hub.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,20 @@ describe('hub command arg filtering', () => {
'--name', 'cw-devbox-1',
'--remote',
'--config', '/tmp/maglev-config-Sbn3W8.yaml',
'--broker-url', 'http://broker:3010',
'--server-url', 'http://server:3010',
'--host', '127.0.0.1',
'--port', '15115'
])

expect(args).toEqual([
'--remote',
'--config', '/tmp/maglev-config-Sbn3W8.yaml',
'--broker-url', 'http://broker:3010',
'--server-url', 'http://server:3010',
'--host', '127.0.0.1',
'--port', '15115'
])
expect(__test__.parseHubArgs(args)).toEqual({
brokerUrl: 'http://broker:3010',
serverUrl: 'http://server:3010',
configPath: '/tmp/maglev-config-Sbn3W8.yaml',
host: '127.0.0.1',
port: '15115'
Expand All @@ -55,21 +55,21 @@ describe('hub command arg filtering', () => {
[
'--remote',
'--config', '/tmp/old.yaml',
'--broker-url', 'http://old-broker:3010',
'--server-url', 'http://old-server:3010',
'--host', '127.0.0.1',
'--port', '15115'
],
[
'--remote',
'--config', '/tmp/new.yaml',
'--broker-url', 'http://new-broker:3010'
'--server-url', 'http://new-server:3010'
]
)

expect(args).toEqual([
'--remote',
'--config', '/tmp/new.yaml',
'--broker-url', 'http://new-broker:3010',
'--server-url', 'http://new-server:3010',
'--host', '127.0.0.1',
'--port', '15115'
])
Expand All @@ -80,7 +80,7 @@ describe('hub command arg filtering', () => {
[
'--remote',
'--config', '/tmp/current.yaml',
'--broker-url', 'http://broker:3010',
'--server-url', 'http://server:3010',
'--port', '15115'
],
['--remote']
Expand All @@ -89,7 +89,7 @@ describe('hub command arg filtering', () => {
expect(args).toEqual([
'--remote',
'--config', '/tmp/current.yaml',
'--broker-url', 'http://broker:3010',
'--server-url', 'http://server:3010',
'--port', '15115'
])
})
Expand Down
Loading
Loading