# Mags

> Secure cloud sandboxes for AI agents and developers

Mags is a CLI, SDK, and API for running scripts on isolated microVMs with workspaces, file upload, cron scheduling, and optional URL access.

## Quickstart

Install the CLI:
```
npm install -g @magpiecloud/mags
```

Authenticate:
```
mags login
```

Run your first script:
```
mags run 'echo Hello World'
```

## Core CLI Commands

- `mags login` — Authenticate and save credentials
- `mags logout` — Remove saved credentials
- `mags whoami` — Check auth status
- `mags new <name> [-p]` — Create a VM sandbox (add -p for S3 data persistence)
- `mags run <script>` — Execute a script on a microVM
- `mags ssh <name>` — Open an SSH session into a sandbox (auto-starts if needed)
- `mags exec <name> <command>` — Run a command on an existing sandbox
- `mags list` — List recent jobs
- `mags logs <name|id>` — View job logs
- `mags status <name|id>` — Check job status
- `mags stop <name|id>` — Stop a running job
- `mags sync <name|id>` — Sync workspace to S3 without stopping the VM
- `mags resize <name> --disk <GB>` — Resize disk for a workspace
- `mags url <name|id> [port]` — Enable public URL access for a running job
- `mags url alias <subdomain> <name>` — Create a stable URL alias
- `mags url alias list` — List URL aliases
- `mags url alias remove <subdomain>` — Remove a URL alias
- `mags set <name|id> --no-sleep` — Never auto-sleep this VM
- `mags set <name|id> --sleep` — Re-enable auto-sleep
- `mags workspace list` — List persistent workspaces
- `mags workspace delete <id>` — Delete workspace and cloud data
- `mags browser [name]` — Start a Chromium browser session (CDP access)

## Run Options

`-w` and `-n` are aliases — both set the job name and workspace ID to the same value.

| Flag | Description |
|------|-------------|
| `-w, --workspace <name>` | Name the job and workspace. Data stays local to the VM only (not synced to S3). Useful for data analysis where you don't need artifacts to persist. Combine with `-p` to sync to S3 and persist. |
| `-n, --name <name>` | Alias for `-w` |
| `-p, --persistent` | Keep VM alive after script and sync workspace to S3. Files persist across runs indefinitely. Required for URL, SSH, and cloud persistence. |
| `--base <workspace>` | Mount an existing workspace read-only as a starting point (OverlayFS). Use with `-w` to fork into a new workspace. |
| `--no-sleep` | Never auto-sleep this VM (requires -p) |
| `-e, --ephemeral` | No workspace, no sync (fastest). Cannot combine with -w, -p, or --base |
| `-f, --file <path>` | Upload a local file into /root/ in the VM (repeatable) |
| `--url` | Enable public URL (requires -p) |
| `--port <port>` | Port to expose (default: 8080) |
| `--disk <GB>` | Custom disk size in GB (default: 2) |
| `--startup-command <cmd>` | Command to run when a VM wakes from sleep |

## Workspace Modes

| Flags | Behavior |
|-------|----------|
| (none) | Ephemeral. No workspace, no persistence. |
| `-w myproject` | Local workspace. Data stays on the VM only (~5 min warm cache after job completes). Not synced to S3. Good for data analysis and throwaway work. |
| `-w myproject -p` | Persistent workspace. VM stays alive, data synced to S3. Files survive across runs indefinitely. |
| `--base golden` | Read-only base image. Changes discarded after run. |
| `--base golden -w fork-1` | Fork: starts from golden, saves changes to fork-1 (local only without -p). |
| `--base golden -w fork-1 -p` | Fork with persistence: starts from golden, saves to fork-1 in S3. |
| `-e` | Explicit ephemeral. Same as no flags but cannot accidentally combine with -w or -p. |

## File Upload

Upload local files into the VM before your script runs. Files appear at `/root/<filename>`.

```
# Single file
mags run -f script.py 'python3 script.py'

# Multiple files
mags run -f analyze.py -f data.csv 'python3 analyze.py'

# With persistent workspace
mags run -w my-project -p -f config.json -f app.js 'cp *.js *.json /root/ && cd /root && npm install'
```

## Cron Scheduling

Schedule recurring jobs with standard cron expressions.

```
# Create a cron job
mags cron add --name "daily-backup" --schedule "0 0 * * *" 'tar czf /root/backup.tar.gz /root/data'

# Create with workspace
mags cron add --name "health-check" --schedule "*/5 * * * *" -w monitors 'curl -sf https://myapp.com/health'

# List cron jobs
mags cron list

# Remove a cron job
mags cron remove <id>

# Enable / disable
mags cron enable <id>
mags cron disable <id>
```

### Cron flags for `add`

| Flag | Description |
|------|-------------|
| `-n, --name` | Cron job name (required) |
| `-s, --schedule` | Cron expression, e.g. "0 */2 * * *" (required) |
| `-w, --workspace` | Workspace ID for storage |
| `-p, --persistent` | Keep VM alive after script |

## Complex Project Workflow

For multi-file projects, the recommended pattern is: create files locally, upload them, then run persistently.

```
# Step 1: Upload files and install deps in a persistent workspace
mags run -w my-project -p -f server.js -f package.json \
  'cp *.js *.json /root/ && cd /root && npm install'

# Step 2: Test run (reuses the persistent workspace)
mags run -w my-project -p 'cd /root && node server.js'

# Step 3: Run with public URL
mags run -w my-project -p --url --port 3000 'cd /root && node server.js'

# Step 4: Schedule recurring run with cron
mags cron add --name "my-server" --schedule "0 */6 * * *" -w my-project \
  'cd /root && node server.js'
```

## Data Analysis Workflow

For throwaway analysis where you need a workspace but don't care about persisting artifacts:

```
# Upload data and analyze — workspace is local only, no S3 overhead
mags run -w analysis -f data.csv 'python3 -c "
import csv
with open(\"/root/data.csv\") as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)
"'

# Results printed to stdout (use `mags logs` to retrieve)
# VM and data cleaned up automatically after ~5 minutes
```

## Ephemeral Mode

For the fastest runs with no workspace at all:
```
mags run -e 'uname -a && df -h'
```

## API

Base URL: `https://api.magpiecloud.com/api/v1`

### Authentication

Use a Bearer token in the Authorization header:
```
Authorization: Bearer <your-api-token>
```

Generate tokens at https://mags.run/tokens or via `mags login`.

### Job Endpoints

- `POST /mags-jobs` — Submit a job (supports `file_ids` array for uploaded files)
- `GET /mags-jobs` — List jobs
- `GET /mags-jobs/{id}/status` — Get job status
- `GET /mags-jobs/{id}/logs` — Get job logs
- `POST /mags-jobs/{id}/access` — Enable URL or SSH access
- `PATCH /mags-jobs/{id}` — Update job settings (startup_command, no_sleep)

### File Endpoints

- `POST /mags-files` — Upload file(s) via multipart/form-data (max 100MB)

### Workspace Endpoints

- `GET /mags-workspaces` — List workspaces
- `DELETE /mags-workspaces/{id}` — Delete workspace and S3 data

### Cron Endpoints

- `POST /mags-cron` — Create a cron job
- `GET /mags-cron` — List cron jobs
- `GET /mags-cron/{id}` — Get a specific cron job
- `PATCH /mags-cron/{id}` — Update a cron job
- `DELETE /mags-cron/{id}` — Delete a cron job

### Submit a Job

```
curl -X POST https://api.magpiecloud.com/api/v1/mags-jobs \
  -H "Authorization: Bearer $MAGS_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "script": "echo Hello World",
    "type": "inline",
    "workspace_id": "myproject",
    "persistent": true
  }'
```

## Python SDK

```
pip install magpie-mags
```

```python
from mags import Mags

mags = Mags()  # uses MAGS_API_TOKEN env var

# Run a script and wait for result
result = mags.run_and_wait('echo Hello World')
print(result['logs'])

# Create a sandbox (local disk)
mags.new('my-project')

# Create with S3 persistence
mags.new('my-project', persistent=True)

# Execute a command on an existing sandbox
result = mags.exec('my-project', 'ls -la /root')
print(result['output'])

# Stop a running job
mags.stop('my-project')

# Enable public URL
info = mags.url('my-project', port=3000)
print(info['url'])

# Find a job by name or workspace
job = mags.find_job('my-project')

# Upload files and run
file_ids = mags.upload_files(['script.py', 'data.csv'])
mags.run('python3 script.py', file_ids=file_ids)

# Cron jobs
mags.cron_create(name='daily-backup', cron_expression='0 0 * * *',
                 script='tar czf backup.tar.gz /data', workspace_id='my-ws')
mags.cron_delete(cron_id)
```

## Node.js SDK

```
npm install @magpiecloud/mags
```

```js
const Mags = require('@magpiecloud/mags');

const mags = new Mags({
  apiToken: process.env.MAGS_API_TOKEN
});

// Run a script
const result = await mags.runAndWait('echo Hello World');
console.log(result.logs);

// Upload files
const fileIds = await mags.uploadFiles(['script.py', 'data.csv']);
const result = await mags.run('python3 script.py', { fileIds });

// Cron jobs
await mags.cronCreate({
  name: 'daily-backup',
  cronExpression: '0 0 * * *',
  script: 'tar czf backup.tar.gz /data',
  workspaceId: 'my-workspace'
});
const { cron_jobs } = await mags.cronList();
await mags.cronDelete(id);
```

## Claude Code Integration

Install the Mags skill for Claude Code:
```
mags setup-claude
```

Then use `/mags <description>` in Claude Code to run scripts in microVMs.

## VM Environment

- Alpine Linux base image
- Hostname: `mags-vm`
- Home directory: `/root`
- JuiceFS mount: `/jfs` (persistent workspaces only)
- Package manager: `apk`
- Pre-installed: curl, wget, git, python3, node, vim, nano

## Cookbook

Ready-to-run recipes at https://mags.run/cookbook including:
- Quick script runs
- Persistent Python/Node workspaces
- Exposing web servers with URLs
- HN Marketing Digest cron job

## Links

- Website: https://mags.run
- Dashboard: https://mags.run/dashboard
- API Tokens: https://mags.run/tokens
- API Reference: https://mags.run/api
- Cookbook: https://mags.run/cookbook
- Claude Skill: https://mags.run/claude-skill
- Documentation: https://mags.run/#cli
