Moved duplicate get_cloud_init_userdata() function from all 4 cloud
provider common.sh files to shared/common.sh. This eliminates 60+ lines
of duplication and centralizes cloud-init configuration.
Changes:
- Added get_cloud_init_userdata() to shared/common.sh with detailed comments
- Removed duplicate function from hetzner/lib/common.sh
- Removed duplicate function from digitalocean/lib/common.sh
- Removed duplicate function from vultr/lib/common.sh
- Removed duplicate function from linode/lib/common.sh
- Added comment that clouds can override if needed
All tests pass (42 passed, 0 failed).
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Enhanced error messages across all cloud providers to be more actionable:
- Changed generic "API token/key is required" to "cannot be empty"
- Added specific authentication failure messages with provider URLs
- Included permission verification hints
- Added non-interactive mode environment variable suggestions
Benefits:
- Users get clear guidance on how to fix authentication issues
- Error messages now include direct links to token management pages
- Better UX for both interactive and non-interactive usage
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Moved duplicate SSH_OPTS constant from all 4 cloud provider common.sh
files to shared/common.sh. This removes 4 lines of duplication and
centralizes SSH configuration.
Changes:
- Added SSH_OPTS to shared/common.sh with comment explaining clouds can override
- Removed SSH_OPTS from hetzner/lib/common.sh
- Removed SSH_OPTS from digitalocean/lib/common.sh
- Removed SSH_OPTS from vultr/lib/common.sh
- Removed SSH_OPTS from linode/lib/common.sh
All tests pass (42 passed, 0 failed).
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Added chmod 600 to all temporary files that contain sensitive data (API keys, tokens, configs):
- ENV_TEMP: 35 files (all agent scripts across 5 clouds)
- OPENCLAW_CONFIG_TEMP: 5 files (already done in previous commit)
- SETTINGS_TEMP: 5 files (Claude Code settings)
- GLOBAL_STATE_TEMP: 5 files (Claude Code global state)
- DOTENV_TEMP: 5 files (NanoClaw .env files)
Total: 55 temp files secured
This prevents race conditions where sensitive data could be read by other users
between mktemp creation (mode 600 by default) and data being written.
Security hardening for task #23.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Created helper functions in shared/common.sh to simplify ensure_ssh_key():
- generate_ssh_key_if_missing(key_path): Generate SSH key if needed
- get_ssh_fingerprint(pub_path): Get MD5 fingerprint
- json_escape(string): JSON-escape strings for API bodies
Refactored ensure_ssh_key() in all cloud providers to use these helpers:
- Reduced function length from 35-52 lines to 24-28 lines
- Eliminated nested conditionals
- Made the code flow more linear and readable
- Centralized SSH key generation and fingerprint logic
Benefits:
- Single source of truth for SSH key operations
- Reduced code duplication (~40 lines per provider → 3 helper functions)
- Easier to maintain and test
- More consistent error handling
All tests pass (42/42).
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Created generic_ssh_wait() in shared/common.sh to eliminate ~100 lines
of duplicated waiting logic across all cloud providers.
Changes:
- Added generic_ssh_wait(ip, ssh_opts, test_cmd, description, max_attempts, interval)
to shared/common.sh
- Refactored verify_server_connectivity() in all clouds to use generic_ssh_wait
- Refactored wait_for_cloud_init() in all clouds to use generic_ssh_wait
Benefits:
- Single source of truth for SSH polling logic
- Consistent error messages across providers
- Reduced code duplication (~20 lines per provider → 2 lines)
- Easier to maintain and test
All tests pass (42/42).
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add set -euo pipefail for strict error handling
- Make color constants readonly (RED, GREEN, YELLOW, NC)
- Make LINODE_API_BASE and SSH_OPTS readonly
- Quote numeric comparison variables in verify_server_connectivity
- Quote numeric comparison variables in wait_for_cloud_init
- Quote numeric comparison variables in create_server
- Quote process IDs and timeout variables in try_oauth_flow
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Added validate_model_id() function to all common.sh files to prevent
command injection via user-supplied MODEL_ID values. MODEL_ID is used
in JSON configs, shell commands, and exported to remote systems, so
validation is critical.
Validation enforces that MODEL_ID contains only safe characters:
- Letters (a-z, A-Z)
- Numbers (0-9)
- Separators: / - _ : .
Rejects dangerous characters like backticks, $(), quotes, semicolons
that could be used for command injection.
Changes:
- Added validate_model_id() to all lib/common.sh files
- Added validation calls after MODEL_ID input in all agent scripts
- Tests pass for all sprite scripts
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Fixed command injection vulnerability in sprite/openclaw.sh where
OPENCLAW_CONFIG was echoed directly into remote shell command with
user-controlled MODEL_ID variable. Changed to use temp file + secure
upload instead of inline echo.
Also added chmod 600 to all OPENCLAW_CONFIG_TEMP files across all
cloud providers (linode, vultr, digitalocean, hetzner, sprite) to
prevent race condition where credentials could be exposed in temp
files before being written.
Changes:
- sprite/openclaw.sh: Replaced echo with temp file + sprite exec -file
- All openclaw.sh: Added chmod 600 after mktemp for credentials
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Open Interpreter provides a natural language interface for computer control.
Works with OpenRouter via OPENAI_BASE_URL=https://openrouter.ai/api/v1.
- Implemented on all 5 clouds: sprite, hetzner, digitalocean, vultr, linode
- Matrix now 7 agents x 5 clouds = 35/35 implemented
Co-authored-by: Sprite <noreply@sprite.dev>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Linode instances via REST API v4, with cloud-init via metadata.user_data.
- linode/lib/common.sh: API wrapper, token management, instance lifecycle
- All 6 agents: claude, openclaw, nanoclaw, aider, goose, codex
Matrix now 6 agents x 5 clouds = 30/30 implemented.
Co-authored-by: Sprite <noreply@sprite.dev>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>