mirror of
https://github.com/OpenRouterTeam/spawn.git
synced 2026-04-28 11:59:29 +00:00
Autonomous refactoring: 5 rounds, ~1,400 lines eliminated, production-ready
Five rounds of autonomous AI agent team refactoring with security fixes, code consolidation, and expanded test coverage.
This commit is contained in:
parent
6ac59e6bb3
commit
3fb2e77b03
47 changed files with 2882 additions and 1853 deletions
456
README.md
456
README.md
|
|
@ -1,74 +1,432 @@
|
|||
# Spawn
|
||||
|
||||
One command to launch any AI coding agent on any cloud, pre-configured with [OpenRouter](https://openrouter.ai).
|
||||
Conjure your agents!
|
||||
|
||||
## Quick Start
|
||||
## Features
|
||||
|
||||
Install the `spawn` CLI:
|
||||
- 🔐 **Automatic OAuth** - Seamless authentication with OpenRouter
|
||||
- 🔄 **Smart Fallback** - Manual API key entry if OAuth fails
|
||||
- 🚀 **One Command Setup** - Get running in minutes
|
||||
- 🔧 **Environment Ready** - Pre-configured shell and dependencies
|
||||
|
||||
## Usage
|
||||
|
||||
#### Claude Code
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/OpenRouterTeam/spawn/main/cli/install.sh | bash
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/sprite/claude.sh)
|
||||
```
|
||||
|
||||
Then launch any agent interactively:
|
||||
#### OpenClaw
|
||||
|
||||
```bash
|
||||
spawn # Interactive picker
|
||||
spawn claude sprite # Launch Claude Code on Sprite
|
||||
spawn aider hetzner # Launch Aider on Hetzner Cloud
|
||||
spawn list # See the full matrix
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/sprite/openclaw.sh)
|
||||
```
|
||||
|
||||
Or run directly without installing:
|
||||
#### NanoClaw
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/{cloud}/{agent}.sh)
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/sprite/nanoclaw.sh)
|
||||
```
|
||||
|
||||
## Matrix
|
||||
|
||||
| | [Sprite](sprite/) | [Hetzner](hetzner/) | [DigitalOcean](digitalocean/) | [Vultr](vultr/) | [Linode](linode/) | [Lambda](lambda/) | [AWS Lightsail](aws-lightsail/) | [GCP](gcp/) | [E2B](e2b/) | [Modal](modal/) |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| **Claude Code** | [launch](sprite/claude.sh) | [launch](hetzner/claude.sh) | [launch](digitalocean/claude.sh) | [launch](vultr/claude.sh) | [launch](linode/claude.sh) | [launch](lambda/claude.sh) | [launch](aws-lightsail/claude.sh) | [launch](gcp/claude.sh) | [launch](e2b/claude.sh) | [launch](modal/claude.sh) |
|
||||
| **OpenClaw** | [launch](sprite/openclaw.sh) | [launch](hetzner/openclaw.sh) | [launch](digitalocean/openclaw.sh) | [launch](vultr/openclaw.sh) | [launch](linode/openclaw.sh) | [launch](lambda/openclaw.sh) | [launch](aws-lightsail/openclaw.sh) | [launch](gcp/openclaw.sh) | [launch](e2b/openclaw.sh) | [launch](modal/openclaw.sh) |
|
||||
| **NanoClaw** | [launch](sprite/nanoclaw.sh) | [launch](hetzner/nanoclaw.sh) | [launch](digitalocean/nanoclaw.sh) | [launch](vultr/nanoclaw.sh) | [launch](linode/nanoclaw.sh) | [launch](lambda/nanoclaw.sh) | [launch](aws-lightsail/nanoclaw.sh) | [launch](gcp/nanoclaw.sh) | [launch](e2b/nanoclaw.sh) | [launch](modal/nanoclaw.sh) |
|
||||
| **Aider** | [launch](sprite/aider.sh) | [launch](hetzner/aider.sh) | [launch](digitalocean/aider.sh) | [launch](vultr/aider.sh) | [launch](linode/aider.sh) | [launch](lambda/aider.sh) | [launch](aws-lightsail/aider.sh) | [launch](gcp/aider.sh) | [launch](e2b/aider.sh) | [launch](modal/aider.sh) |
|
||||
| **Goose** | [launch](sprite/goose.sh) | [launch](hetzner/goose.sh) | [launch](digitalocean/goose.sh) | [launch](vultr/goose.sh) | [launch](linode/goose.sh) | [launch](lambda/goose.sh) | [launch](aws-lightsail/goose.sh) | [launch](gcp/goose.sh) | [launch](e2b/goose.sh) | [launch](modal/goose.sh) |
|
||||
| **Codex CLI** | [launch](sprite/codex.sh) | [launch](hetzner/codex.sh) | [launch](digitalocean/codex.sh) | [launch](vultr/codex.sh) | [launch](linode/codex.sh) | [launch](lambda/codex.sh) | [launch](aws-lightsail/codex.sh) | [launch](gcp/codex.sh) | [launch](e2b/codex.sh) | [launch](modal/codex.sh) |
|
||||
| **Open Interpreter** | [launch](sprite/interpreter.sh) | [launch](hetzner/interpreter.sh) | [launch](digitalocean/interpreter.sh) | [launch](vultr/interpreter.sh) | [launch](linode/interpreter.sh) | [launch](lambda/interpreter.sh) | [launch](aws-lightsail/interpreter.sh) | [launch](gcp/interpreter.sh) | [launch](e2b/interpreter.sh) | [launch](modal/interpreter.sh) |
|
||||
| **Gemini CLI** | [launch](sprite/gemini.sh) | [launch](hetzner/gemini.sh) | [launch](digitalocean/gemini.sh) | [launch](vultr/gemini.sh) | [launch](linode/gemini.sh) | [launch](lambda/gemini.sh) | [launch](aws-lightsail/gemini.sh) | [launch](gcp/gemini.sh) | [launch](e2b/gemini.sh) | [launch](modal/gemini.sh) |
|
||||
| **Amazon Q** | [launch](sprite/amazonq.sh) | [launch](hetzner/amazonq.sh) | [launch](digitalocean/amazonq.sh) | [launch](vultr/amazonq.sh) | [launch](linode/amazonq.sh) | [launch](lambda/amazonq.sh) | [launch](aws-lightsail/amazonq.sh) | [launch](gcp/amazonq.sh) | [launch](e2b/amazonq.sh) | [launch](modal/amazonq.sh) |
|
||||
| **Cline** | [launch](sprite/cline.sh) | [launch](hetzner/cline.sh) | [launch](digitalocean/cline.sh) | [launch](vultr/cline.sh) | [launch](linode/cline.sh) | [launch](lambda/cline.sh) | [launch](aws-lightsail/cline.sh) | [launch](gcp/cline.sh) | [launch](e2b/cline.sh) | [launch](modal/cline.sh) |
|
||||
|
||||
**10 agents x 10 clouds = 100 combinations.** Every script injects OpenRouter credentials automatically.
|
||||
|
||||
## How It Works
|
||||
|
||||
Each script:
|
||||
1. Authenticates with the cloud provider
|
||||
2. Provisions a server/sandbox
|
||||
3. Installs the agent + dependencies
|
||||
4. Gets your OpenRouter API key (OAuth or manual)
|
||||
5. Injects OpenRouter env vars into the shell
|
||||
6. Drops you into an interactive session
|
||||
|
||||
## Non-Interactive Mode
|
||||
|
||||
Skip all prompts by setting env vars:
|
||||
#### Aider
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/sprite/aider.sh)
|
||||
```
|
||||
|
||||
#### Goose
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/sprite/goose.sh)
|
||||
```
|
||||
|
||||
#### Codex CLI
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/sprite/codex.sh)
|
||||
```
|
||||
|
||||
#### Open Interpreter
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/sprite/interpreter.sh)
|
||||
```
|
||||
|
||||
### Non-Interactive Mode
|
||||
|
||||
For automation or CI/CD, set environment variables:
|
||||
|
||||
#### Claude Code
|
||||
|
||||
```bash
|
||||
SPRITE_NAME=dev-mk1 \
|
||||
OPENROUTER_API_KEY=sk-or-v1-xxxxx \
|
||||
{CLOUD_NAME_VAR}=dev-mk1 \
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/{cloud}/claude.sh)
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/sprite/claude.sh)
|
||||
```
|
||||
|
||||
See each cloud's [README](sprite/README.md) for provider-specific env vars.
|
||||
|
||||
## Self-Improving
|
||||
#### OpenClaw
|
||||
|
||||
```bash
|
||||
./improve.sh # agent team fills gaps + discovers new agents/clouds
|
||||
./improve.sh --loop # continuous improvement cycles
|
||||
SPRITE_NAME=dev-mk1 \
|
||||
OPENROUTER_API_KEY=sk-or-v1-xxxxx \
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/sprite/openclaw.sh)
|
||||
```
|
||||
|
||||
Uses [Claude Code Agent Teams](https://code.claude.com/docs/en/agent-teams) to coordinate parallel work.
|
||||
#### NanoClaw
|
||||
|
||||
```bash
|
||||
SPRITE_NAME=dev-mk1 \
|
||||
OPENROUTER_API_KEY=sk-or-v1-xxxxx \
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/sprite/nanoclaw.sh)
|
||||
```
|
||||
|
||||
**Environment Variables:**
|
||||
- `SPRITE_NAME` - Name for the sprite (skips prompt)
|
||||
- `OPENROUTER_API_KEY` - Skip OAuth and use this API key directly
|
||||
|
||||
---
|
||||
|
||||
## Hetzner Cloud
|
||||
|
||||
Spawn agents on [Hetzner Cloud](https://www.hetzner.com/cloud/) servers. No `hcloud` CLI needed — uses the Hetzner REST API directly.
|
||||
|
||||
### Usage
|
||||
|
||||
#### Claude Code
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/hetzner/claude.sh)
|
||||
```
|
||||
|
||||
#### OpenClaw
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/hetzner/openclaw.sh)
|
||||
```
|
||||
|
||||
#### NanoClaw
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/hetzner/nanoclaw.sh)
|
||||
```
|
||||
|
||||
#### Aider
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/hetzner/aider.sh)
|
||||
```
|
||||
|
||||
#### Goose
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/hetzner/goose.sh)
|
||||
```
|
||||
|
||||
#### Codex CLI
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/hetzner/codex.sh)
|
||||
```
|
||||
|
||||
#### Open Interpreter
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/hetzner/interpreter.sh)
|
||||
```
|
||||
|
||||
### Non-Interactive Mode
|
||||
|
||||
```bash
|
||||
HETZNER_SERVER_NAME=dev-mk1 \
|
||||
HCLOUD_TOKEN=your-hetzner-api-token \
|
||||
OPENROUTER_API_KEY=sk-or-v1-xxxxx \
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/hetzner/claude.sh)
|
||||
```
|
||||
|
||||
**Environment Variables:**
|
||||
- `HETZNER_SERVER_NAME` - Name for the server (skips prompt)
|
||||
- `HCLOUD_TOKEN` - Hetzner Cloud API token (skips prompt, saved to `~/.config/spawn/hetzner.json`)
|
||||
- `OPENROUTER_API_KEY` - Skip OAuth and use this API key directly
|
||||
- `HETZNER_SERVER_TYPE` - Server type (default: `cx22`)
|
||||
- `HETZNER_LOCATION` - Datacenter location (default: `fsn1`)
|
||||
|
||||
---
|
||||
|
||||
## DigitalOcean
|
||||
|
||||
Spawn agents on [DigitalOcean](https://www.digitalocean.com/) Droplets via REST API.
|
||||
|
||||
### Usage
|
||||
|
||||
#### Claude Code
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/digitalocean/claude.sh)
|
||||
```
|
||||
|
||||
#### OpenClaw
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/digitalocean/openclaw.sh)
|
||||
```
|
||||
|
||||
#### NanoClaw
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/digitalocean/nanoclaw.sh)
|
||||
```
|
||||
|
||||
#### Aider
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/digitalocean/aider.sh)
|
||||
```
|
||||
|
||||
#### Goose
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/digitalocean/goose.sh)
|
||||
```
|
||||
|
||||
#### Codex CLI
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/digitalocean/codex.sh)
|
||||
```
|
||||
|
||||
#### Open Interpreter
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/digitalocean/interpreter.sh)
|
||||
```
|
||||
|
||||
### Non-Interactive Mode
|
||||
|
||||
```bash
|
||||
DO_DROPLET_NAME=dev-mk1 \
|
||||
DO_API_TOKEN=your-digitalocean-api-token \
|
||||
OPENROUTER_API_KEY=sk-or-v1-xxxxx \
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/digitalocean/claude.sh)
|
||||
```
|
||||
|
||||
**Environment Variables:**
|
||||
- `DO_DROPLET_NAME` - Name for the droplet (skips prompt)
|
||||
- `DO_API_TOKEN` - DigitalOcean API token (skips prompt, saved to `~/.config/spawn/digitalocean.json`)
|
||||
- `OPENROUTER_API_KEY` - Skip OAuth and use this API key directly
|
||||
- `DO_DROPLET_SIZE` - Droplet size (default: `s-2vcpu-2gb`)
|
||||
- `DO_REGION` - Datacenter region (default: `nyc3`)
|
||||
|
||||
---
|
||||
|
||||
## Vultr
|
||||
|
||||
Spawn agents on [Vultr](https://www.vultr.com/) Cloud Compute instances via REST API.
|
||||
|
||||
### Usage
|
||||
|
||||
#### Claude Code
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/vultr/claude.sh)
|
||||
```
|
||||
|
||||
#### OpenClaw
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/vultr/openclaw.sh)
|
||||
```
|
||||
|
||||
#### NanoClaw
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/vultr/nanoclaw.sh)
|
||||
```
|
||||
|
||||
#### Aider
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/vultr/aider.sh)
|
||||
```
|
||||
|
||||
#### Goose
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/vultr/goose.sh)
|
||||
```
|
||||
|
||||
#### Codex CLI
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/vultr/codex.sh)
|
||||
```
|
||||
|
||||
#### Open Interpreter
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/vultr/interpreter.sh)
|
||||
```
|
||||
|
||||
### Non-Interactive Mode
|
||||
|
||||
```bash
|
||||
VULTR_SERVER_NAME=dev-mk1 \
|
||||
VULTR_API_KEY=your-vultr-api-key \
|
||||
OPENROUTER_API_KEY=sk-or-v1-xxxxx \
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/vultr/claude.sh)
|
||||
```
|
||||
|
||||
**Environment Variables:**
|
||||
- `VULTR_SERVER_NAME` - Name for the instance (skips prompt)
|
||||
- `VULTR_API_KEY` - Vultr API key (skips prompt, saved to `~/.config/spawn/vultr.json`)
|
||||
- `OPENROUTER_API_KEY` - Skip OAuth and use this API key directly
|
||||
- `VULTR_PLAN` - Instance plan (default: `vc2-1c-2gb`)
|
||||
- `VULTR_REGION` - Datacenter region (default: `ewr`)
|
||||
|
||||
---
|
||||
|
||||
## Linode (Akamai)
|
||||
|
||||
Spawn agents on [Linode](https://www.linode.com/) instances via REST API.
|
||||
|
||||
### Usage
|
||||
|
||||
#### Claude Code
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/linode/claude.sh)
|
||||
```
|
||||
|
||||
#### OpenClaw
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/linode/openclaw.sh)
|
||||
```
|
||||
|
||||
#### NanoClaw
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/linode/nanoclaw.sh)
|
||||
```
|
||||
|
||||
#### Aider
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/linode/aider.sh)
|
||||
```
|
||||
|
||||
#### Goose
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/linode/goose.sh)
|
||||
```
|
||||
|
||||
#### Codex CLI
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/linode/codex.sh)
|
||||
```
|
||||
|
||||
#### Open Interpreter
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/linode/interpreter.sh)
|
||||
```
|
||||
|
||||
### Non-Interactive Mode
|
||||
|
||||
```bash
|
||||
LINODE_SERVER_NAME=dev-mk1 \
|
||||
LINODE_API_TOKEN=your-linode-api-token \
|
||||
OPENROUTER_API_KEY=sk-or-v1-xxxxx \
|
||||
bash <(curl -fsSL https://openrouter.ai/lab/spawn/linode/claude.sh)
|
||||
```
|
||||
|
||||
**Environment Variables:**
|
||||
- `LINODE_SERVER_NAME` - Label for the Linode (skips prompt)
|
||||
- `LINODE_API_TOKEN` - Linode API token (skips prompt, saved to `~/.config/spawn/linode.json`)
|
||||
- `OPENROUTER_API_KEY` - Skip OAuth and use this API key directly
|
||||
- `LINODE_TYPE` - Instance type (default: `g6-standard-1`)
|
||||
- `LINODE_REGION` - Datacenter region (default: `us-east`)
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
Spawn uses a **shared library pattern** to reduce code duplication across cloud providers:
|
||||
|
||||
### Library Structure
|
||||
|
||||
```
|
||||
spawn/
|
||||
shared/
|
||||
common.sh # Provider-agnostic utilities (logging, OAuth, SSH helpers)
|
||||
{cloud}/
|
||||
lib/common.sh # Cloud-specific functions (sources shared/common.sh)
|
||||
{agent}.sh # Agent deployment scripts
|
||||
```
|
||||
|
||||
### How It Works
|
||||
|
||||
1. **`shared/common.sh`** - Core utilities used by all clouds:
|
||||
- Color logging (`log_info`, `log_warn`, `log_error`)
|
||||
- Safe input handling (`safe_read`)
|
||||
- OAuth flow for OpenRouter authentication
|
||||
- Network utilities (`nc_listen`, `open_browser`)
|
||||
- SSH key management and connectivity helpers
|
||||
- Security validation (`validate_model_id`, `json_escape`)
|
||||
|
||||
2. **`{cloud}/lib/common.sh`** - Cloud-specific extensions:
|
||||
- Sources `shared/common.sh` first
|
||||
- Adds provider-specific functions (API wrappers, provisioning logic)
|
||||
- Examples: `sprite/lib/common.sh` adds Sprite CLI functions, `hetzner/lib/common.sh` adds Hetzner API functions
|
||||
|
||||
3. **Agent scripts** - Combine shared utilities with cloud-specific provisioning:
|
||||
- Source their cloud's `lib/common.sh`
|
||||
- Use shared functions for authentication and setup
|
||||
- Use cloud functions for server provisioning
|
||||
- Deploy and configure the specific agent
|
||||
|
||||
### Benefits
|
||||
|
||||
- **DRY (Don't Repeat Yourself)** - OAuth, logging, and SSH logic are written once
|
||||
- **Consistency** - All scripts use the same patterns for authentication and error handling
|
||||
- **Maintainability** - Bug fixes in `shared/common.sh` benefit all cloud providers
|
||||
- **Extensibility** - Adding a new cloud only requires writing provider-specific logic
|
||||
|
||||
---
|
||||
|
||||
## Security
|
||||
|
||||
### API Token Storage
|
||||
|
||||
Spawn stores cloud provider API tokens and OpenRouter API keys locally in JSON files at `~/.config/spawn/`:
|
||||
|
||||
- `hetzner.json` - Hetzner Cloud API token
|
||||
- `digitalocean.json` - DigitalOcean API token
|
||||
- `vultr.json` - Vultr API key
|
||||
- `linode.json` - Linode API token
|
||||
- OpenRouter API keys stored in shell config files (`~/.bashrc`, `~/.zshrc`)
|
||||
|
||||
**Security Posture:**
|
||||
- All token files are created with `chmod 600` (user read/write only)
|
||||
- Tokens are stored in **plaintext** - not encrypted at rest
|
||||
- Security relies on filesystem permissions and OS user isolation
|
||||
|
||||
**Recommendations:**
|
||||
1. **Protect your user account** - Use strong passwords, disk encryption, and secure your SSH keys
|
||||
2. **Use dedicated API tokens** - Create tokens specifically for Spawn with minimal required permissions
|
||||
3. **Rotate tokens regularly** - Revoke and regenerate API tokens periodically
|
||||
4. **Multi-user systems** - On shared machines, be aware that root users can read these files
|
||||
5. **Backup security** - Ensure backups of `~/.config/` are encrypted
|
||||
|
||||
**Why plaintext?**
|
||||
- Simplicity and compatibility across all Unix-like systems
|
||||
- File permissions (`600`) provide adequate protection for single-user machines
|
||||
- Encryption at rest would require key management, adding complexity without significant security benefit for typical use cases
|
||||
- Cloud providers recommend similar approaches for CLI tools (AWS CLI, gcloud, etc.)
|
||||
|
||||
**Alternative approaches:**
|
||||
- For higher security requirements, consider using environment variables instead of saved tokens
|
||||
- Pass `OPENROUTER_API_KEY`, `HCLOUD_TOKEN`, etc. as environment variables on each run
|
||||
- Use OS credential stores (Keychain on macOS, Secret Service on Linux) - requires additional dependencies
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue