From bf9499bac0b42cd71bca9209ba9cf6a117b2e410 Mon Sep 17 00:00:00 2001 From: MermaidMan Date: Fri, 22 May 2026 17:17:51 +0000 Subject: [PATCH] add documentation trio: README (casual), DETAILS (technical), LLMs (agent reference) --- DETAILS.md | 418 +++++++++++++++++++++++++++++++++++++++++++++++++++++ LLMs.md | 272 ++++++++++++++++++++++++++++++++++ README.md | 262 ++++++++++++++++----------------- 3 files changed, 821 insertions(+), 131 deletions(-) create mode 100644 DETAILS.md create mode 100644 LLMs.md diff --git a/DETAILS.md b/DETAILS.md new file mode 100644 index 0000000..422e28f --- /dev/null +++ b/DETAILS.md @@ -0,0 +1,418 @@ +# DETAILS.md — Technical Reference + +Companion to [README.md](README.md). This file contains the full technical details that would normally live in a professional README — architecture, API surface, configuration schema, build system, and development workflows. Intended for developers, contributors, and automated tooling. + +--- + +## Overview + +`obm` is a Go CLI that generates Terraform-compatible `.env` files through an interactive walkthrough or a YAML config file. It validates API credentials against live endpoints before writing config, and wraps Terraform lifecycle commands (init, apply, destroy). + +- **Language:** Go 1.22 +- **Module:** `github.com/openboatmobile/obm` +- **Dependencies:** `gopkg.in/yaml.v3` (sole external dependency) +- **Binary:** Single statically-linked binary, zero runtime dependencies +- **Current version:** See `VERSION` file (0.1.0 at time of writing) + +--- + +## Architecture + +``` +obm/ +├── cmd/obm/main.go # CLI entry point, subcommand routing +├── internal/ +│ ├── config/ +│ │ ├── config.go # Config struct, GetValue/SetValue +│ │ ├── deployment.go # DeploymentConfig, AdminUser(), MonthlyCostEstimate() +│ │ ├── dotenv.go # DotEnvFile parser (round-trip .env read) +│ │ ├── dotenv_writer.go # WriteDotEnv — grouped, commented .env output +│ │ ├── schema.go # VarDef schema (all TF_VAR_ variables), VarGroup enum +│ │ ├── tfvars.go # WriteTfVars — HCL-format tfvars output +│ │ ├── yaml.go # YAMLConfig struct, LoadYAMLConfig(), ToDeploymentConfig() +│ │ ├── config_test.go +│ │ └── yaml_test.go +│ ├── deploy/ +│ │ └── deploy.go # Walkthrough orchestrator (Run, RunFromFile, RunWithConfig) +│ ├── destroy/ +│ │ ├── destroy.go # Terraform destroy with state parsing, confirmation +│ │ └── destroy_test.go +│ ├── inference/ +│ │ ├── client.go # HTTP client, ValidateAPIKey(), ValidationResult +│ │ ├── inference.go # Provider enum, ProviderConfig, FallbackChain, DefaultGLMConfig +│ │ ├── client_test.go +│ │ └── inference_test.go +│ ├── prompt/ +│ │ ├── prompt.go # Terminal I/O: Select, Confirm, Input, Password, color helpers +│ │ └── prompt_test.go +│ ├── provider/ +│ │ ├── provider.go # Provider interface, BaseProvider, Registry, Register/Get +│ │ ├── import.go # Provider registration (blank import guidance) +│ │ ├── hetzner/ +│ │ │ ├── hetzner.go # HetznerProvider: API validation, SSH key listing +│ │ │ └── hetzner_test.go +│ │ └── provider_test.go +│ ├── terraform/ +│ │ └── terraform.go # Runner: Init, Plan, Apply, Destroy wrappers +│ └── validation/ +│ ├── validation.go # Check interface, Runner, CheckResult, Status enum +│ └── validation_test.go +├── scripts/ +│ ├── install.sh # curl | sh installer +│ └── release.sh # Tag + push release automation +├── .github/workflows/ +│ ├── ci.yml # Test + build on push/PR +│ └── release.yml # Cross-compile + GitHub Release on tag +├── Makefile # Build, test, lint, cross-compile targets +├── Dockerfile # Multi-stage: golang:1.22-alpine → alpine:3.20 +├── deploy.yaml.example # Full YAML config reference +├── CHANGELOG.md +├── CONTRIBUTING.md +└── VERSION # Single line, e.g. "0.1.0" +``` + +--- + +## CLI Interface + +### Subcommands + +| Command | Flags | Description | +|---------|-------|-------------| +| `obm deploy` | `--config ` | Interactive walkthrough (default) or non-interactive from YAML | +| `obm validate` | `--env-file ` | Load `.env`, check required vars, validate API keys | +| `obm status` | — | Show deployment state (not yet implemented) | +| `obm destroy` | — | Confirmation prompt → `terraform destroy` → state cleanup | +| `obm version` | — | Print version with commit hash and build time | +| `obm help` | — | Print usage | + +### Build-time variables + +Injected via `-ldflags` at build time: + +| Variable | Flag | Example | +|----------|------|---------| +| `main.version` | `-X main.version=0.1.0` | Semver from `VERSION` file | +| `main.gitCommit` | `-X main.gitCommit=abc1234` | Short commit SHA | +| `main.buildTime` | `-X main.buildTime=2026-05-22T15:30:00Z` | UTC ISO timestamp | + +--- + +## Deploy Walkthrough Flow + +The `obm deploy` interactive flow runs 8 steps in sequence: + +1. **Framework** — Select Hermes or OpenClaw. Sets framework-specific defaults on `DeploymentConfig`. +2. **Cloud Provider** — Hetzner or DigitalOcean. +3. **Provider Token** — Enter API token. For Hetzner, validates against `/server_types` endpoint and lists SSH keys. +4. **SSH Key** — Select from keys found on the provider, or enter manually. +5. **Server Config** — Name, location/region, server type/droplet size. +6. **Inference Provider** — ZAI, Venice, OpenRouter. Enter API key. Validates against `/models` endpoint. +7. **Tailscale** — Optional VPN setup. Auth key and tailnet domain. +8. **Discord** — Optional bot integration. Bot token, server ID, user IDs. + +Final step: summary display with cost estimate → confirm → write `.env` → optionally run `terraform init && terraform apply`. + +### Framework-specific defaults + +**Hermes:** +- `DockerEnabled = true` +- `VeniceBaseURL = "https://api.venice.ai/api/v1"` +- `GatewayAllowAllUsers = true` +- `DiscordAutoThread = true` + +**OpenClaw:** +- `OpenClawVersion = "lts"` +- `NodeVersion = "22"` +- `EnableSwap = true`, `SwapSizeGB = 2` +- `EnableFail2ban = true`, `EnableUnattendedUpgrades = true` + +--- + +## Key Types + +### DeploymentConfig (`internal/config/deployment.go`) + +Central struct holding all walkthrough choices. 30+ fields covering framework, provider, server, inference, Tailscale, Discord, and gateway configuration. + +Key methods: +- `AdminUser() string` — returns framework name ("hermes" or "openclaw") +- `MonthlyCostEstimate() string` — returns price string based on server type/droplet size + +Package-level helper functions (not methods, because `DeploymentConfig` is in `config` but called from `deploy`): +- `config.LocationOrRegion(cfg)` — Hetzner location or DO region +- `config.ServerTypeOrDroplet(cfg)` — server type or droplet size +- `config.SSHKeySummary(cfg)` — masked SSH key display + +### DotEnvFile (`internal/config/dotenv.go`) + +Round-trip parser for `.env` files: +- `ParseDotEnv(path)` — parse `TF_VAR_`-prefixed env file +- `env.GetVar(name)` — lookup with and without `TF_VAR_` prefix +- `env.Values` — raw `map[string]string` + +### VarDef Schema (`internal/config/schema.go`) + +Complete schema of all Terraform variables with metadata: + +```go +type VarDef struct { + Name string // TF variable name (e.g. "cloud_provider") + Type ValueType // string, number, bool, list + Default string + Required bool + Sensitive bool + Description string + Group VarGroup // Section for organized output + EnvComment string // Additional .env hint +} +``` + +Variables are grouped: `PROVIDER`, `PROVIDER — Hetzner`, `PROVIDER — DigitalOcean`, `SERVER CONFIGURATION`, `SSH CONFIGURATION`, `API KEYS`, `MODEL CONFIGURATION`, `DISCORD`, `TAILSCALE`, `HERMES-SPECIFIC`, `OPENCLAW-SPECIFIC`, `SECURITY`, `PROJECT METADATA`. + +### InferenceClient (`internal/inference/client.go`) + +HTTP client for validating inference API keys: +- `ValidateAPIKey(ctx, provider, apiKey)` — hits `/models` endpoint, checks for HTTP 200 +- Returns `ValidationResult{Valid, ErrorMessage, ModelCount, Latency}` +- 30-second default timeout + +### Provider Interface (`internal/provider/provider.go`) + +```go +type Provider interface { + Name() string + ProviderName() string + Validate(ctx context.Context) error + Checks(ctx context.Context) []validation.Check + TokenEnvKey() string + SetToken(token string) + GetToken() string +} +``` + +Provider registry pattern: `Register(name, factory)` / `Get(name)`. Hetzner implementation at `internal/provider/hetzner/`. + +### Validation Framework (`internal/validation/validation.go`) + +Structured check system with `Runner`: + +```go +type Check interface { + Name() string + Category() CheckCategory + Run(ctx context.Context) CheckResult +} +``` + +Status values: `PASS`, `FAIL`, `WARN`, `SKIP`, `ERROR`. +Categories: `Credentials`, `Connectivity`, `SSH Keys`, `Server Config`, `Quotas`, `Account`. + +--- + +## Inference Providers + +Currently supported: + +| Provider | Enum | Base URL | Auth | +|----------|------|----------|------| +| Z.ai | `ProviderZAI` | `https://api.z.ai/api/coding/paas/v4` | `GLM_API_KEY` env | +| Venice.ai | `ProviderVenice` | `https://api.venice.ai/api/v1` | `VENICE_API_KEY` env | +| OpenRouter | `ProviderOpenRouter` | `https://openrouter.ai/api/v1` | `OPENROUTER_API_KEY` env | + +Fallback chains: ZAI → Venice → OpenRouter (for GLM models); Venice → OpenRouter. + +`DefaultGLMConfig()` sets `MaxTokens=16384` to prevent the over-compression bug where Venice defaults to 131K. + +--- + +## .env Generation + +`WriteDotEnv()` in `internal/config/dotenv_writer.go` generates the `.env` file from a `Config`. Output format: + +- Header comment with usage instructions +- Variables grouped by `VarGroup`, each with description comment +- `TF_VAR_` prefix on all variable names +- JSON arrays for SSH keys: `TF_VAR_ssh_key_names='["key-name"]'` (single-quoted shell string containing JSON) +- Sensitive values get `YOUR_..._HERE` placeholders if empty +- `WriteTfVars()` generates HCL-format `terraform.tfvars` as an alternative + +### Variable flow + +User input → `DeploymentConfig` → `.env` (TF_VAR_ prefixed) → `source .env` → Terraform reads env vars → `templatefile()` → cloud-init → server provisioning. + +--- + +## YAML Config (Non-interactive Mode) + +`LoadYAMLConfig(path)` parses a YAML file into `YAMLConfig`, then `ToDeploymentConfig()` converts to `DeploymentConfig`. Schema: + +```yaml +framework: hermes | openclaw +provider: + name: hetzner | digitalocean + token: "..." + ssh: + names: [...] + fingerprints: [...] +server: + name: "..." + location: "ash" | "fsn1" | "nbg1" | "hel1" + type: "cpx21" | ... +inference: + provider: venice | openrouter | openai | anthropic | custom + api_key: "..." + primary_model: "..." +tailscale: + enabled: true + auth_key: "..." + tailnet: "..." +discord: + enabled: true + bot_token: "..." + server_id: "..." +``` + +Full example: [`deploy.yaml.example`](deploy.yaml.example). + +--- + +## Build System + +### Makefile targets + +| Target | Description | +|--------|-------------| +| `make build` | Build binary for current platform | +| `make test` | Run tests with race detection and coverage | +| `make lint` | `go vet` + `gofmt` | +| `make vet` | Run `go vet` | +| `make fmt` | Format with `gofmt` | +| `make clean` | Remove binary and coverage files | +| `make cross-compile` | Build for linux-amd64, linux-arm64, darwin-arm64, windows-amd64, windows-arm64 | +| `make version` | Print VERSION file contents | + +### Version injection + +```bash +VERSION=$(cat VERSION) +GIT_COMMIT=$(git rev-parse --short HEAD) +BUILD_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ") +LDFLAGS="-s -w -X main.version=$VERSION -X main.gitCommit=$GIT_COMMIT -X main.buildTime=$BUILD_TIME" +go build $LDFLAGS -o obm ./cmd/obm +``` + +### Docker + +Multi-stage Dockerfile: `golang:1.22-alpine` (build) → `alpine:3.20` (runtime). Non-root user `obm:1000`. Entry point: `obm --help`. + +--- + +## CI/CD + +### CI (`ci.yml`) + +Triggers on push/PR to main. Runs: `go vet` → `go test` → `gofmt` check → build. Build matrix: linux/darwin/windows × amd64/arm64 (excludes darwin/amd64). + +### Release (`release.yml`) + +Triggers on tag push (`v*`). Builds cross-compiled binaries, creates archives (.tar.gz for Unix, .zip for Windows), generates SHA256 checksums, creates GitHub Release with upload. + +### Release process + +```bash +./scripts/release.sh v0.2.0 +# This: validates version → runs tests → updates VERSION → commits → tags → pushes tag +``` + +Pre-release versions (containing hyphen, e.g. `v1.0.0-beta.1`) are marked as pre-release on GitHub. + +--- + +## Cost Estimation + +`DeploymentConfig.MonthlyCostEstimate()` maps server types to price strings. + +Hetzner prices (current): + +| Type | Price | +|------|-------| +| cx22 | €3.79/mo | +| cx23 | €5.83/mo | +| cpx21 | €4.49/mo | +| cpx31 | €8.98/mo | +| cpx41 | €17.96/mo | + +DigitalOcean prices: + +| Size | Price | +|------|-------| +| s-1vcpu-1gb | $6/mo | +| s-1vcpu-2gb | $12/mo | +| s-2vcpu-4gb | $24/mo | +| s-4vcpu-8gb | $48/mo | +| g-2vcpu-8gb | $63/mo | + +--- + +## Terraform Integration + +`obm` generates the `.env` file that Terraform expects. The actual Terraform configs live in the separate [openboatmobile-ai](https://github.com/openboatmobile/openboatmobile-ai) repo. + +The `internal/terraform/terraform.go` wrapper provides: +- `Runner.Init()` — `terraform init -input=false` +- `Runner.Plan(destroy bool)` — `terraform plan` (with optional `-destroy` flag) +- `Runner.Apply()` — `terraform apply -auto-approve` +- `Runner.Destroy()` — `terraform destroy -auto-approve` + +All commands run in the `WorkDir` and capture combined output. + +### Variable flow to cloud-init + +User sets `TF_VAR_*` env vars → sourced from `.env` → Terraform reads them → injected into cloud-init templates via `templatefile()` → written to server during provisioning. + +### Cloud-init outputs + +**Hermes** (`userdata-hermes.tpl`): +- `/home//.hermes/.env` — API keys, Discord token, gateway token +- `/home//.hermes/config.yaml` — model config, Discord channels +- `/home//.hermes/SOUL.md` — agent personality template +- `/home//docker-compose.yml` — Docker mode only +- `/etc/systemd/system/hermes.service` — systemd unit +- `/usr/local/bin/hermes-health-check.sh` — diagnostic script + +**OpenClaw** (`userdata-openclaw.tpl`): +- `/etc/openclaw.env` — secrets (0600, root-owned) +- `/home//.openclaw/openclaw.json` — full config +- `/etc/systemd/system/openclaw-gateway.service` +- `/usr/local/bin/openclaw-health-check.sh` + +--- + +## Destroy Flow + +`obm destroy` reads `terraform.tfstate` to list resources, shows them, asks for confirmation, runs `terraform destroy`, then cleans up state files unless `--keep-state` is set. + +--- + +## Testing + +```bash +make test # Full suite with race detection + coverage +go test ./... # Without make +go test -v -race -coverprofile=coverage.out ./... +``` + +Test coverage includes: config parsing, dotenv round-tripping, YAML loading, inference client validation, Hetzner provider validation, destroy workflow, prompt helpers. + +--- + +## Contributing + +See [CONTRIBUTING.md](CONTRIBUTING.md) for the full guide including development setup, PR checklist, and release process. + +Key points: +- Go 1.22+ required +- Run `make lint test` before pushing +- Never push directly to `main` +- Feature branches: `git checkout -b ` diff --git a/LLMs.md b/LLMs.md new file mode 100644 index 0000000..550f4b3 --- /dev/null +++ b/LLMs.md @@ -0,0 +1,272 @@ +# LLMs.md — Agent Reference for obm + +This file is written for AI agents (LLMs, coding assistants, autonomous agents) who need to understand the `obm` project and work with their human to deploy, configure, or develop it. If you're reading this, you're probably an agent trying to help someone. Here's what you need to know. + +--- + +## What obm Is + +`obm` (OpenBoatMobile) is a Go CLI tool that deploys AI agents to cloud infrastructure. It generates Terraform-compatible `.env` files through an interactive walkthrough or a YAML config, validates API credentials against live endpoints, and wraps Terraform lifecycle commands. + +Think of it as: "Terraform for AI agents, with a friendly wizard instead of a 94-line `.env` template." + +--- + +## Quick Orientation + +| Fact | Value | +|------|-------| +| Language | Go 1.22 | +| Module path | `github.com/openboatmobile/obm` | +| External deps | `gopkg.in/yaml.v3` only | +| Binary type | Static, zero runtime deps | +| Version file | `VERSION` (single line, e.g. `0.1.0`) | +| Current version | 0.1.0 | +| License | MIT | + +**Repos:** +- `obm` (this repo) — the Go CLI +- `openboatmobile-ai` — the Terraform configs that `obm` generates `.env` files for +- `openboatmobile` (private) — live deployment with keys, do NOT touch + +--- + +## Package Map + +Use this to find what you need: + +``` +cmd/obm/main.go → Entry point, subcommand routing, build-time version vars +internal/config/ → All configuration types and I/O: + config.go → Config struct, GetValue/SetValue + deployment.go → DeploymentConfig (30+ fields), cost estimation + schema.go → VarDef schema (all Terraform variables), VarGroup enum + dotenv.go → DotEnvFile round-trip parser + dotenv_writer.go → WriteDotEnv — grouped .env output + tfvars.go → WriteTfVars — HCL-format output + yaml.go → YAMLConfig, LoadYAMLConfig, ToDeploymentConfig +internal/deploy/deploy.go → Walkthrough orchestrator (Run, RunFromFile, RunWithConfig) +internal/destroy/destroy.go → Terraform destroy with state parsing +internal/inference/ → Inference provider validation: + client.go → HTTP client, ValidateAPIKey, ValidationResult + inference.go → Provider enum, ProviderConfig, FallbackChain +internal/prompt/prompt.go → Terminal I/O: Select, Confirm, Input, Password, colors +internal/provider/ → Cloud provider abstraction: + provider.go → Provider interface, BaseProvider, Registry + hetzner/hetzner.go → Hetzner Cloud API: token validation, SSH key listing +internal/terraform/terraform.go → Runner: Init, Plan, Apply, Destroy +internal/validation/validation.go → Check interface, Runner, CheckResult, Status enum +``` + +--- + +## CLI Commands Your Human Might Ask About + +| Command | What it does | When they'd use it | +|---------|-------------|-------------------| +| `obm deploy` | Interactive 8-step walkthrough | First-time setup or redeploy | +| `obm deploy --config deploy.yaml` | Non-interactive from YAML | CI/CD, automation, repeat deploys | +| `obm validate` | Check `.env` + validate API keys | "Did I configure this right?" | +| `obm destroy` | Tear down infrastructure | "I'm done with this server" | +| `obm version` | Print version with commit/build info | Debugging | + +--- + +## Deploy Walkthrough Steps + +When your human runs `obm deploy`, they'll go through these in order: + +1. **Framework** — Hermes or OpenClaw +2. **Cloud Provider** — Hetzner or DigitalOcean +3. **Provider Token** — API key for their cloud provider (validated live) +4. **SSH Key** — Select from provider or enter manually +5. **Server Config** — Name, location/region, size +6. **Inference Provider** — ZAI, Venice, or OpenRouter (API key validated live) +7. **Tailscale** — Optional VPN (auth key + tailnet) +8. **Discord** — Optional bot integration (token, server ID, user IDs) + +After all 8 steps: summary + cost estimate → confirm → `.env` written → optional `terraform init && apply`. + +--- + +## What Your Human Needs Before Deploying + +Tell them to have these ready: + +1. **Cloud provider account** with API token (Hetzner: console.hetzner.cloud → Security → API Tokens; DO: cloud.digitalocean.com → API) +2. **SSH public key** uploaded to their cloud provider +3. **Inference provider API key** (Venice, OpenRouter, or ZAI) +4. *(Optional)* Tailscale auth key +5. *(Optional)* Discord bot token + server ID + +--- + +## Framework Differences + +When your human asks "Hermes or OpenClaw?": + +**Hermes Agent** — if they want: +- Discord chat, voice, web search, many integrations +- Python-based, Docker or direct install +- More configurable (gateway, allowed users, auto-threading) + +**OpenClaw** — if they want: +- Simpler setup, fewer moving parts +- Node.js-based +- Built-in security (fail2ban, unattended upgrades, swap) + +--- + +## Configuration Files + +### `.env` (Terraform input) + +All variables prefixed with `TF_VAR_`. Generated by `obm deploy` or `WriteDotEnv()`. This is the primary output of the tool. Groups: PROVIDER, SERVER, SSH, API KEYS, MODEL, DISCORD, TAILSCALE, HERMES-SPECIFIC, OPENCLAW-SPECIFIC, SECURITY, PROJECT. + +Special format for SSH keys: `TF_VAR_ssh_key_names='["key-name"]'` — single-quoted shell string containing JSON array. + +### `deploy.yaml` (Non-interactive input) + +Full schema in `deploy.yaml.example`. Key sections: + +```yaml +framework: hermes +provider: + name: hetzner + token: "..." + ssh: + names: ["my-key"] +server: + name: "agent" + location: "ash" + type: "cpx21" +inference: + provider: venice + api_key: "..." + primary_model: "zai-org-glm-5" +tailscale: + enabled: true + auth_key: "..." +discord: + enabled: false +``` + +--- + +## Build Commands + +```bash +make build # Build for current platform +make test # Test with race detection + coverage +make lint # go vet + gofmt +make cross-compile # All platforms: linux-{amd64,arm64}, darwin-arm64, windows-{amd64,arm64} +make clean # Remove binary + coverage +``` + +From source without make: +```bash +go build -o obm ./cmd/obm +go test ./... +go vet ./... +``` + +Version injection at build time: +```bash +go build -ldflags "-s -w -X main.version=$(cat VERSION) -X main.gitCommit=$(git rev-parse --short HEAD) -X main.buildTime=$(date -u +%Y-%m-%dT%H:%M:%SZ)" -o obm ./cmd/obm +``` + +--- + +## Common Tasks Your Human Might Ask For + +### "Help me deploy an agent" + +1. Make sure they have API keys ready (see "What Your Human Needs" above) +2. Run `obm deploy` +3. Walk them through the 8 steps if they have questions +4. The cost estimate at the end is approximate — cloud providers may vary + +### "Check if my config is valid" + +```bash +obm validate +``` + +This loads `.env`, checks required variables, and validates inference API keys against live endpoints. + +### "I want to tear down my server" + +```bash +obm destroy +``` + +It asks for confirmation. It reads `terraform.tfstate` to show what will be destroyed. + +### "Set up CI/CD deployment" + +1. Create a `deploy.yaml` from `deploy.yaml.example` +2. Store secrets in your CI secrets manager +3. In pipeline: `obm deploy --config deploy.yaml` + +### "Add a new cloud provider" + +1. Create `internal/provider//.go` implementing the `Provider` interface +2. Register via `init()`: `provider.Register("", func() provider.Provider { return New() })` +3. Add provider-specific fields to `DeploymentConfig` +4. Update `schema.go` with new `VarGroup` and `VarDef` entries +5. Add step function in `deploy.go` + +### "Add a new inference provider" + +1. Add `Provider` constant to `internal/inference/inference.go` +2. Add case to `Info()` method (name, env var, base URL) +3. Add case to `UnmarshalText()` for YAML parsing +4. Add case to `setAuthHeaders()` in `client.go` if auth differs from Bearer token +5. Update `AllProviders()` return slice + +--- + +## Known Limitations + +- `obm status` is stubbed — not yet functional +- DigitalOcean provider validation not yet implemented (only Hetzner) +- No resumable deploy — interrupted walkthrough = start over +- No cost preview before the summary step +- `openai` and `anthropic` inference providers listed in schema but not fully implemented in the inference client (only ZAI, Venice, OpenRouter have live validation) + +--- + +## Relationship to Terraform Configs + +`obm` generates the `.env` file. The Terraform configs that consume it live in the `openboatmobile-ai` repo. The variable schema in `internal/config/schema.go` must stay in sync with the Terraform variable definitions in `openboatmobile-ai/variables-*.tf`. + +If you add a variable to the Terraform configs, you must also add a corresponding `VarDef` to `schema.go`. + +--- + +## Testing Strategy + +- Unit tests in each package (`*_test.go`) +- Hetzner provider tested with mock HTTP servers (`WithHTTPClient` + `WithBaseURL` options) +- Inference client tested with mock `/models` endpoints +- Run with: `make test` or `go test -v -race -coverprofile=coverage.out ./...` + +--- + +## File Locations Summary + +| File | Purpose | +|------|---------| +| `VERSION` | Single-line semver string | +| `Makefile` | Build, test, lint, cross-compile targets | +| `Dockerfile` | Multi-stage build for containerized obm | +| `deploy.yaml.example` | Full YAML config reference | +| `scripts/install.sh` | `curl \| sh` installer | +| `scripts/release.sh` | Tag + push release automation | +| `.github/workflows/ci.yml` | Test + build on push/PR | +| `.github/workflows/release.yml` | Cross-compile + GitHub Release on tag | +| `CHANGELOG.md` | Version history | +| `CONTRIBUTING.md` | Dev setup, PR checklist, release process | +| `README.md` | User-facing overview (casual tone) | +| `DETAILS.md` | Full technical reference | +| `LLMs.md` | This file — agent-oriented reference | diff --git a/README.md b/README.md index fd77885..3c2a530 100644 --- a/README.md +++ b/README.md @@ -1,167 +1,167 @@ -# obm - OpenBoatMobile Infrastructure CLI +# obm 🚢 -A CLI tool for deploying AI agents on cloud infrastructure with Terraform. +**Deploy your own AI agent to the cloud in about five minutes.** -## Installation +No YAML file editing. No guessing if your API keys work. No 94-line config templates. +Just answer a few questions and your agent is live. -### Quick Install (Linux/macOS) +--- + +## What is this? + +`obm` is a command-line tool that walks you through setting up an AI agent on cloud infrastructure. It asks you things like "which cloud provider?" and "which AI model?" — then validates your answers on the spot, writes the config, and hands it off to Terraform to build everything. + +It supports two agent frameworks: + +- **Hermes Agent** (by Nous Research) — a Python-based agent with Discord chat, voice, web search, and a ton of integrations. Think of it as a personal AI assistant you can talk to. +- **OpenClaw** — a Node.js-based agent with a simpler setup. Good if you want something lighter. + +Both run on either **Hetzner Cloud** (cheap, EU-based) or **DigitalOcean** (more regions). + +--- + +## Quick start + +### Install + +**Mac or Linux (one command):** ```bash curl -fsSL https://raw.githubusercontent.com/openboatmobile/obm/main/scripts/install.sh | sh ``` -Install a specific version: +**Or download a binary** from the [releases page](https://github.com/openboatmobile/obm/releases/latest) and put it somewhere on your PATH. + +**Or build from source** (requires Go 1.22+): ```bash -curl -fsSL https://raw.githubusercontent.com/openboatmobile/obm/main/scripts/install.sh | sh -s -- v1.2.3 +git clone https://github.com/openboatmobile/obm.git +cd obm +make build ``` -### Manual Install - -Download the latest release for your platform from [GitHub Releases](https://github.com/openboatmobile/obm/releases/latest): - -| Platform | Architecture | Download | -|----------|-------------|----------| -| Linux | x86_64 (amd64) | `obm-linux-amd64.tar.gz` | -| Linux | ARM64 | `obm-linux-arm64.tar.gz` | -| macOS | Apple Silicon (arm64) | `obm-darwin-arm64.tar.gz` | -| Windows | x86_64 (amd64) | `obm-windows-amd64.zip` | -| Windows | ARM64 | `obm-windows-arm64.zip` | - -**Linux/macOS:** -```bash -# Download and extract -curl -sL https://github.com/openboatmobile/obm/releases/latest/download/obm-linux-amd64.tar.gz | tar xz -# Or for ARM64: -# curl -sL https://github.com/openboatmobile/obm/releases/latest/download/obm-linux-arm64.tar.gz | tar xz - -chmod +x obm -sudo mv obm /usr/local/bin/ -``` - -**Windows (PowerShell):** -```powershell -# Download and extract -Invoke-WebRequest -Uri https://github.com/openboatmobile/obm/releases/latest/download/obm-windows-amd64.zip -OutFile obm.zip -Expand-Archive obm.zip -# Add to PATH as needed -``` - -### From Source +### Deploy your agent ```bash -go build -o obm ./cmd/obm +obm deploy ``` -## Usage +That's it. You'll get an interactive walkthrough that looks like this: -### Interactive Mode (Default) +``` +🚢 OpenBoatmobile — Deploy your AI agent -Run the interactive wizard to configure your deployment: +Step 1: Agent Framework + [1] Hermes Agent (Nous Research) — Python-based, highly configurable + [2] OpenClaw — Node.js-based, simpler setup -```bash -./obm deploy +Step 2: Cloud Provider + [1] Hetzner Cloud — from €4.49/mo (recommended, ~70% cheaper) + [2] DigitalOcean — from $6/mo (wider region availability) + +Step 3: Provider API Token + Get yours at: https://console.hetzner.cloud/ → Security → API Tokens + Token: ******** + ✓ Token validated + +... ``` -The wizard will guide you through: -1. Agent framework selection (Hermes or OpenClaw) -2. Cloud provider (Hetzner or DigitalOcean) -3. Server configuration -4. Inference provider (Venice, OpenRouter, OpenAI, Anthropic, or Custom) -5. Optional: Tailscale VPN, Discord integration +At the end, you'll see a summary with cost estimate and get asked to confirm. Say yes, and `obm` writes your config and kicks off Terraform. -### Non-Interactive Mode (CI/CD) +### Other commands -For automated deployments, use a YAML configuration file: - -```bash -./obm deploy --config deploy.yaml -``` - -See `deploy.yaml.example` for a complete configuration reference. - -## Commands - -| Command | Description | +| Command | What it does | |---------|-------------| -| `deploy` | Deploy an AI agent (interactive or --config for CI/CD) | -| `validate` | Check configuration and API credentials | -| `status` | Show current infrastructure state | -| `destroy` | Tear down provisioned infrastructure | -| `version` | Print version | -| `help` | Show help message | +| `obm deploy` | Interactive walkthrough to set up a new agent | +| `obm validate` | Checks your existing config and API keys | +| `obm status` | Shows the state of your current deployment | +| `obm destroy` | Tears down your infrastructure (asks first, don't worry) | +| `obm version` | Prints the version | -## Configuration File Format +### Non-interactive mode (for automation) -The YAML configuration file supports the following structure: - -```yaml -# Required: Agent framework -framework: hermes # or openclaw - -# Required: Cloud provider -provider: - name: hetzner # or digitalocean - token: "your-api-token" - ssh: - names: ["my-ssh-key"] # Hetzner - # fingerprints: ["aa:bb:cc:dd"] # DigitalOcean - -# Server configuration -server: - name: "my-agent" - location: "ash" # Hetzner: ash, fsn1, nbg1, hel1 - type: "cpx21" - -# Required: Inference provider -inference: - provider: venice # venice, openrouter, openai, anthropic, custom - api_key: "your-api-key" - primary_model: "zai-org-glm-5" - -# Optional: Tailscale VPN -tailscale: - enabled: true - auth_key: "tskey-auth-..." - tailnet: "mytailnet" - -# Optional: Discord integration -discord: - enabled: true - bot_token: "" - server_id: "" -``` - -## Example Workflows - -### Local Development +If you're running this in CI/CD or just don't want the prompts: ```bash -# Interactive setup -./obm deploy - -# Validate your .env file -./obm validate --env-file .env +obm deploy --config deploy.yaml ``` -### CI/CD Pipeline +See [`deploy.yaml.example`](deploy.yaml.example) for the full config file format. -```bash -# Create deploy.yaml from your secrets manager -# Then run non-interactive deployment -./obm deploy --config deploy.yaml -``` +--- -### GitOps Setup +## What you'll need -1. Store `deploy.yaml` in your repository (use template with placeholders) -2. Use a secrets manager for sensitive values -3. In CI: - ```bash - envsubst < deploy.yaml.template > deploy.yaml - ./obm deploy --config deploy.yaml - ``` +Before running `obm deploy`, have these ready: + +1. **A cloud provider account** — [Hetzner Cloud](https://console.hetzner.cloud/) or [DigitalOcean](https://cloud.digitalocean.com/). Hetzner is cheaper; DigitalOcean has more data center locations. +2. **An API token** from your cloud provider. You can generate one in their dashboard. +3. **An AI model API key** — Venice AI, OpenRouter, OpenAI, or Anthropic. This is the "brain" your agent will use. +4. **An SSH public key** uploaded to your cloud provider (so you can log into your server later). + +Optional but recommended: +- **Tailscale** account for VPN access to your server +- **Discord bot token** if you want your agent to chat on Discord + +--- + +## How much does it cost? + +The server cost depends on your cloud provider and server size. `obm` shows you the estimated monthly cost before you commit. + +**Rough starting points:** + +| Provider | Smallest option | Good for | +|----------|----------------|----------| +| Hetzner | €4.49/mo (2 vCPU, 4 GB RAM) | Most agents | +| DigitalOcean | $6/mo (1 vCPU, 1 GB RAM) | Light use | + +The AI model API costs are separate and depend on your usage. + +--- + +## What happens under the hood? + +`obm` generates a `.env` file that [Terraform](https://terraform.io) reads to provision your server, install the agent software, and configure everything. You don't need to know Terraform — `obm` handles it. + +The Terraform configs live in the [openboatmobile-ai](https://github.com/openboatmobile/openboatmobile-ai) repo. `obm` is the friendly CLI wrapper around them. + +--- + +## Project status + +`obm` is actively developed and functional for the core deploy workflow. Here's what works and what's coming: + +**Working now:** +- Interactive deploy walkthrough (8 steps) +- API key validation for cloud providers and inference providers +- SSH key listing from Hetzner +- `.env` file generation +- Config validation (`obm validate`) +- Infrastructure teardown (`obm destroy`) +- Non-interactive mode with YAML config +- Cross-compiled binaries (Linux, macOS, Windows) +- `curl | sh` installer + +**Coming soon:** +- `obm status` — SSH health checks on your deployment +- DigitalOcean provider validation +- Cost estimation display +- Resumable deploy (pick up where you left off) + +--- + +## For developers + +Building, testing, and contributing — see [DETAILS.md](DETAILS.md) for the full technical reference and [CONTRIBUTING.md](CONTRIBUTING.md) for the contribution guide. + +## For AI agents + +If you're an AI agent reading this to learn about the project, check out [LLMs.md](LLMs.md) — it's written specifically for you. + +--- ## License -MIT \ No newline at end of file +MIT