evroc-skills logo

evroc-skills

0

Agent skills for managing evroc cloud infrastructure: compute, networking, storage, IAM, load balancers, Think Models, and Rancher deployments.

7 skills

evroc-compute

Use when the user wants to create, list, inspect, update, or delete evroc VMs, disks, snapshots, or placement groups. Requires the evroc CLI. Do NOT trigger for networking, IAM, storage buckets, load balancers, or Think model operations.

# evroc-compute Manage evroc compute resources through the `evroc compute` CLI. Covers VMs, disks, snapshots, and placement groups. Read the full CLI reference when needed: [`references/cli-reference.md`](references/cli-reference.md). ## When to Use - User wants to create, list, inspect, update, or delete a VM on evroc - User wants to create or manage disks on evroc - User wants to take or manage disk snapshots - User wants to create placement groups for VM scheduling - User mentions "evroc" with "VM", "instance", "disk", "snapshot", or "compute" **Do NOT use for:** networking (public IPs, security groups, subnets, VPCs), IAM, storage (buckets), load balancers, or Think models. ## Prerequisites Ensure the evroc CLI is installed: ```bash bash scripts/check-cli.sh ``` Then authenticate: ```bash bash scripts/check-auth.sh ``` If it fails, authenticate: ```bash evroc login ``` ## Virtual Machines Requires: a pre-existing boot disk, a flavor, a subnet, and a zone. If the subnet or security group does not exist yet, create them via evroc-networking first. Read networking details when needed: [`references/networking.md`](references/networking.md) or via **evroc-networking** skill. ### List VMs ```bash # Names only evroc compute vm list # YAML output with full details evroc compute vm list -o yaml # Filter by label evroc compute vm list -l "team in (frontend,backend)" ``` ### Get VM details ```bash evroc compute vm get <vm-name> -o yaml ``` ### Create a VM Requires: a pre-existing boot disk, a flavor, a subnet, and a zone. **Before creating the boot disk, ask the user which OS image they want.** Available images are listed in [`references/cli-reference.md`](references/cli-reference.md#available-disk-images) (e.g. `ubuntu-minimal.24-04.1`, `ubuntu.24-04.1`, `rocky.9-6.1`). Do not guess — present the options and confirm the user's choice. ```bash # 1. Create boot disk (ask user which image — see references/cli-reference.md) evroc compute disk create my-boot-disk \ --image=<image-name> \ --size-amount=50 \ --size-unit=GB \ --zone=a # 2. Create VM evroc compute vm create my-vm \ --vm-virtual-resources-ref=a1a.m \ --disk=my-boot-disk \ --boot-from=true \ --subnet=default-se-sto-a \ --security-group=my-sg \ --ssh-authorized-key="ssh-ed25519 AAAA... user@example.com" \ --zone=a \ --running=true ``` **Completion criterion:** The VM has a `Ready` condition with `status: "True"` **and** `virtualMachineStatus: "Running"`. The VM goes through several conditions before Ready (`SecurityGroupsReady`, `NetworkingReady`, `VMIsRunning`, `Ready`), so checking just `reason:` or grepping `status:` returns intermediate states like `SecurityGroupsReady`. ### Poll for VM readiness: - Checking readiness — prefer Python, with jq only as last resort: The status: key appears at both the top level and nested under conditions[] in the YAML/JSON output. Grepping for status: matches the wrong field and returns the condition's status ("True") instead of the condition's type ("Ready"). Prefer a robust parser — try Python first, and fall back to jq only when you know the JSON does not contain unescaped control characters: #### Preferred — Python using YAML output (avoids unescaped JSON control chars) evroc compute vm get <vm-name> -o yaml | python3 -c "import sys, yaml; d=yaml.safe_load(sys.stdin); print([c['status'] for c in d['status']['conditions'] if c['type']=='Ready'][0])" #### Alternative — jq when JSON is clean evroc compute vm get <vm-name> -o json | jq -r '.status.conditions[] | select(.type=="Ready") | .status' For VM running state, use .status.virtualMachineStatus. Why Python first? The evroc output can contain unescaped control characters inside string fields such as cloudInitUserData; these make the JSON technically invalid and break jq with: jq: parse error: Invalid string: control characters from U+0000 through U+001F must be escaped. YAML mode plus python3 + pyyaml handles this gracefully. Do NOT grep for `status:` or `reason:` in YAML — there are multiple condition entries and grep matches the first one (e.g. `SecurityGroupsReady`), not the overall Ready state. > **Note on cloud-init timing:** The `Ready` and `Running` conditions indicate the VM > is up, but cloud-init may still be executing `runcmd` blocks (package installs, > downloads, service setup). These can take several minutes after the VM becomes > `Ready`. An application service may not be available even though the VM reports > `Running`. If the user expects a service to be reachable, wait and probe the > application port rather than relying solely on the VM's `Ready` condition. To probe whether an application port is open and accepting connections: ```bash # Poll the application port — redirect nc output to /dev/null so the # comparison variable only contains OPEN/CLOSED, not nc's stdout. for i in $(seq 1 30); do if nc -z -w3 <ip> <port> >/dev/null 2>&1; then echo "Port <port> is OPEN (attempt $i)" break fi echo "Port <port> closed (attempt $i), waiting..." sleep 10 done ``` > **Warning:** Do NOT capture `nc` output into the comparison variable without > redirecting to `/dev/null`. `nc` prints "Connection to ... succeeded!" to stdout, > which contaminates the variable and breaks string comparisons — the loop never > breaks even when the port is open. ### Create a VM with cloud-init ```bash evroc compute vm create my-vm \ --vm-virtual-resources-ref=a1a.m \ --disk=my-boot-disk \ --boot-from=true \ --subnet=default-se-sto-a \ --zone=a \ --cloud-init-user-data="#cloud-config packages: - nginx runcmd: - systemctl enable nginx - systemctl start nginx" ``` **SSH users and cloud-init interaction:** If you do not provide custom cloudInit userdata, the platform creates an `evroc-user` for you and attaches the `--ssh-authorized-key` to it. When you provide custom cloudInit with a `users:` block, `evroc-user` is **NOT** created automatically, and the `--ssh-authorized-key` passed via CLI or API is **NOT** automatically attached to your custom users. To get SSH access with custom users, you must include the SSH key in that user's `ssh_authorized_keys` field within the cloudInit config itself. **Alternative:** omit the `users:` block from your cloudInit, let the platform create `evroc-user`, and SSH as that user. **Cloud-init `write_files` ordering:** The `write_files` module runs **before** `users` and `runcmd`. Do **not** use it to create files in directories that are created by `runcmd` (e.g., `mkdir -p /opt/app`), and do **not** set `owner` to a user defined in the `users:` block because that user does not exist yet when `write_files` executes. To avoid silently failed files, use `runcmd` with `tee` or `cat` instead, or use `bootcmd` to create directories early and let `write_files` create files in those directories. **Runtime version compatibility:** When the user's application has runtime dependencies (Java, Python, Node.js, Go, etc.), verify that the runtime version. **do NOT** assume installed in the cloud-init `packages:` or `runcmd:` block is compatible with the application version before deploying. A version mismatch causes the service to crash silently — the VM is `Ready`, the port probe shows `CLOSED`, but there's no obvious error without SSH access. HALT: Before running ANY evroc compute create/update command, you MUST complete the following 6 steps in order. Do NOT proceed to disk/VM creation until the user has explicitly confirmed the compatibility message in Step 4. 1. Identify the application's runtime requirement (e.g. Java xx for an application, Python 3.11 for a specific framework, Node 20 for an app) - NOTE: These are only examples. 2. **Web search** the application's official documentation to find the exact **minimum runtime version** required for that application version. Treat system knowledge as potentially stale — documentation is the source of truth **You MUST cite the exact URL and the verbatim text/quote from that documentation that states the runtime requirement. NEVER proceed from just memory.** 3. Once you have the runtime requirement, check the **OS image's package manager** (e.g. `openjdk-21-jre-headless` for Ubuntu 24.04, `python3.11` for Ubuntu 22.04, `nodejs` from NodeSource). Present the exact package name (or alternative installation method if apt does not provide it) to the user. 4. If the OS image's default package manager cannot satisfy the required version, add a step in `runcmd` to install the correct runtime (e.g. Adoptium tarball, NodeSource repo, deadsnakes PPA, `pyenv`, `rustup`, language-specific version managers). 5. Present the compatibility check to the user in this exact format: "**[INSERT APPLICATION]** requires **[INSERT RUNTIME] [VERSION]**. **[INSERT OS IMAGE]** provides **[INSERT PACKAGE]** which gives **[INSERT INSTALLED VERSION]** — confirm this is what you want?" 6. **Only proceed with the cloud-init config once the user confirms.** Common runtime version pitfalls: > > | Runtime | Issue | Fix | > |---|---|---| > | **Python** | App needs 3.11+ but image has 3.10 | Install via `deadsnakes` PPA, `pyenv`, or official Python source tarball | > | **Node.js** | App needs Node 20 but image has Node 18 | Use NodeSource setup script, `nvm`, or official Node.js binaries | > | **Go** | Binary compiled for wrong arch | Ensure `GOARCH` matches VM arch (amd64 vs arm64) | > | **Ruby** | App needs Ruby 3.3 but image has 3.0 | Use `rvm` or `rbenv` in `runcmd` | > | **Rust** | App needs recent cargo/rustc | Install via `rustup` in `runcmd` | ### Update a VM ```bash # Attach an additional disk evroc compute vm update my-vm --append --disk=extra-disk --boot-from=false # Attach a security group evroc compute vm update my-vm --append --security-group=additional-sg # Stop / start evroc compute vm update my-vm --running=false evroc compute vm update my-vm --running=true # Assign a public IP evroc compute vm update my-vm --public-ip=my-public-ip-name ``` ## Security Groups VMs require security groups for network access. Without explicitly assigned security groups, the VM's firewall will drop all traffic. ### Default security groups Each project includes four preconfigured security groups: | Security Group | Allows | |---|---| | `default-allow-egress` | All outbound traffic (IPv4 + IPv6). VMs are auto-added unless you specify custom groups. | | `default-allow-intra-vpc` | All inbound + outbound traffic within the default VPC (inter-VM communication). | | `default-allow-web-protocols` | HTTP (TCP 80) + HTTPS (TCP 443) from any IP. | | `default-allow-ssh` | SSH (TCP 22) from any IP. Must be explicitly added. | ### List existing security groups Before creating a new security group, check what already exists: ```bash evroc networking securitygroup list ``` ### Create a security group ```bash evroc networking securitygroup create my-sg ``` ### Add rules to a security group ```bash # Allow SSH from anywhere (IPv4) evroc networking securitygroup addrule my-sg \ --name=allow-ssh-ipv4 \ --direction=Ingress \ --remote-ip-or-cidr=0.0.0.0/0 \ --port=22 \ --protocol=TCP # Allow HTTPS from anywhere (IPv6) evroc networking securitygroup addrule my-sg \ --name=allow-https-ipv6 \ --direction=Ingress \ --remote-ip-or-cidr=::/0 \ --port=443 \ --protocol=TCP ``` > **Note:** IPv4 and IPv6 rules are separate — create one rule per protocol if you need both. > **Application ports:** In addition to SSH (22), the user's application likely needs > its own ingress rule for the port it listens on. The default security groups only > cover SSH (22), HTTP (80), and HTTPS (443) — any other port requires a custom rule. > > **Port lookup workflow:** When the user mentions an application by name (e.g. > PostgreSQL, Redis, Minecraft, Grafana), look up its default port(s) and confirm > with the user before creating security group rules: > > | Common application | Default port(s) | Protocol | > |---|---|---| > | PostgreSQL | 5432 | TCP | > | MySQL | 3306 | TCP | > | Redis | 6379 | TCP | > | MongoDB | 27017 | TCP | > | Elasticsearch | 9200 | TCP | > | Grafana | 3000 | TCP | > | Prometheus | 9090 | TCP | > | Node.js / Express | 3000 | TCP | > | Django / Flask | 8000 | TCP | > | FastAPI | 8000 | TCP | > | Nginx (non-default) | 8080 | TCP | > | RabbitMQ | 5672 (AMQP), 15672 (mgmt) | TCP | > | Kafka | 9092 | TCP | > | MinIO | 9000, 9001 (console) | TCP | > | etcd | 2379 | TCP | > | Consul | 8500 (HTTP), 8300 (RPC) | TCP | > > This table is not exhaustive. If the application isn't listed or the user has > customized the port, **ask the user to confirm the exact port(s)** rather than > assuming the default. Present what you found and get explicit confirmation: > "PostgreSQL typically listens on port 5432 — is that correct for your setup?" > > If the application isn't in the table above and the user doesn't know the port, > use a web search to look up the application's default port(s), then confirm > the findings with the user before creating the rule. ### Attach a security group to a VM ```bash # Append (keeps existing groups) evroc compute vm update my-vm --append --security-group=my-sg # Replace all security groups (must specify ALL groups the VM should have) evroc compute vm update my-vm \ --security-group=default-allow-egress \ --security-group=my-sg ``` > **Warning:** Without `--append`, the update replaces all existing security groups. You must specify the complete list. ### Pre-flight checklist before VM creation Before creating a VM, verify or create these prerequisites: 1. **Boot disk** — create and wait for `Ready` condition (see [Disks](#disks)) 2. **Security group** — either use a default (e.g. `default-allow-ssh`) or create one with `evroc networking securitygroup create` + `addrule`. If the user needs SSH access, ensure a rule allows TCP 22. If they need web traffic, allow 80/443. If their application uses a custom port, create an ingress rule for it. 3. **Subnet** — use the default `default-<region>-<zone>` or create one 4. **SSH key** — confirm or ask the user for their SSH public key. Without custom cloudInit, the platform creates `evroc-user` and attaches the key. With custom cloudInit containing a `users:` block, `evroc-user` is not created and the key is not automatically attached — the user must embed the key in the cloudInit `ssh_authorized_keys` field for the intended user. ### Delete a VM HALT: Before running ANY `evroc compute vm delete` command, you MUST output this message to the user VERBATIM before doing anything else: "This will permanently delete the Virtual Machine '<vm-name>' and cannot be undone. Deleting a Virtual Machine also deletes all associated Public IPs and Disks." Then: 1. Run a pre-deletion inspection to identify what's running on the VM: ```bash bash scripts/inspect-vm-services.sh <vm-name> [ssh-user] [ssh-key-path] ``` - If the agent is running ON the VM, the script detects this automatically and warns: "SELF-DESTRUCTION DETECTED — this session will be terminated." - If the agent is remote, the script SSHes into the VM and lists running systemd services and Docker containers. - If the VM is stopped or SSH fails, the script exits 0 with a note — non-blocking, proceed with the standard warning. 2. Present the inspection results to the user alongside the resource list (name, type, project). Include any services/containers that will be lost. 3. If the inspection detected self-destruction, output this additional warning VERBATIM: "WARNING: This agent is running on the VM you are about to delete. Deleting it will terminate this session immediately and any unsaved work will be lost. Consider migrating the agent to another VM before proceeding." 4. Ask the user to confirm: "Type 'yes' to confirm deletion of '<vm-name>'." 5. Wait for the user to explicitly type 'yes' Do NOT run the delete command until the user explicitly confirms. A silence or ambiguous response is NOT confirmation. ```bash evroc compute vm delete my-vm --force ``` **Completion criterion:** `evroc compute vm get <vm-name>` returns `not found`. ## Disks ### List disks ```bash evroc compute disk list evroc compute disk list -o yaml evroc compute disk list -l "environment=production" ``` ### Get disk details ```bash evroc compute disk get <disk-name> -o yaml ``` ### Create a disk From an image (ask the user which one — see [`references/cli-reference.md`](references/cli-reference.md#available-disk-images)): ```bash evroc compute disk create my-disk \ --image=<image-name> \ --size-amount=100 \ --size-unit=GB \ --zone=a ``` **Completion criterion:** Wait for the disk's `Ready` condition to be `True` before creating a VM from it. Poll with: ```bash evroc compute disk get <disk-name> -o json | jq -r '.status.conditions[] | select(.type=="Ready") | .status' # Returns "True" when ready. Do NOT grep for "status:" in YAML output — # the top-level status: field and the nested condition status: field both # match, causing false readings. ``` From a snapshot: ```bash evroc compute disk create my-disk \ --snapshot=my-snapshot \ --zone=a ``` ### Delete a disk HALT: Before running ANY `evroc compute disk delete` command, you MUST output this message to the user VERBATIM before doing anything else: "This will permanently delete your Disk '<disk-name>' and cannot be undone." Then: 1. List the specific resource(s) that will be deleted (name, type, project) 2. Ask the user to confirm: "Type 'yes' to confirm deletion of '<disk-name>'." 3. Wait for the user to explicitly type 'yes' Do NOT run the delete command until the user explicitly confirms. A silence or ambiguous response is NOT confirmation. ```bash evroc compute disk delete <disk-name> --force ``` **Completion criterion:** `evroc compute disk get <disk-name>` returns `not found`. ## Hotswap Disk Attachments Hotswap disk attachments allow attaching a disk to a running VM without rebooting. See [`references/cli-reference.md`](references/cli-reference.md#hotswap-disk-attachments) for full flag details. ### Create a hotswap disk attachment ```bash evroc compute hotswapdiskattachment create my-hda \ --disk-ref=my-disk \ --vm-ref=my-vm ``` ### List hotswap disk attachments ```bash evroc compute hotswapdiskattachment list ``` ### Delete a hotswap disk attachment HALT: Before running ANY `evroc compute hotswapdiskattachment delete` command, you MUST output this message to the user VERBATIM before doing anything else: "This will permanently delete the Disk Attachment '<attachment-name>' and cannot be undone." Then: 1. List the specific resource(s) that will be deleted (name, type, project) 2. Ask the user to confirm: "Type 'yes' to confirm deletion of '<attachment-name>'." 3. Wait for the user to explicitly type 'yes' Do NOT run the delete command until the user explicitly confirms. A silence or ambiguous response is NOT confirmation. ```bash evroc compute hotswapdiskattachment delete my-hda --force ``` ## Snapshots ### Create a snapshot ```bash evroc compute snapshot create my-snapshot --disk-ref=my-disk ``` ### List snapshots ```bash evroc compute snapshot list ``` ### Get snapshot details ```bash evroc compute snapshot get <snapshot-name> -o yaml ``` ### Delete a snapshot HALT: Before running ANY `evroc compute snapshot delete` command, you MUST output this message to the user VERBATIM before doing anything else: "This will permanently delete your Snapshot '<snapshot-name>' and cannot be undone." Then: 1. List the specific resource(s) that will be deleted (name, type, project) 2. Ask the user to confirm: "Type 'yes' to confirm deletion of '<snapshot-name>'." 3. Wait for the user to explicitly type 'yes' Do NOT run the delete command until the user explicitly confirms. A silence or ambiguous response is NOT confirmation. ```bash evroc compute snapshot delete <snapshot-name> --force ``` ## Placement Groups ### Create a placement group ```bash evroc compute placementgroup create my-pg \ --strategy-type=spread \ --zone=a ``` ### List placement groups ```bash evroc compute placementgroup list ``` ### Assign a VM to a placement group ```bash evroc compute vm create my-vm \ --vm-virtual-resources-ref=a1a.m \ --disk=my-boot-disk \ --boot-from=true \ --subnet=default-se-sto-a \ --placement-group=my-pg \ --zone=a ``` ### Delete a placement group HALT: Before running ANY `evroc compute placementgroup delete` command, you MUST output this message to the user VERBATIM before doing anything else: "This will permanently delete the Placement Group '<pg-name>' and cannot be undone." Then: 1. List the specific resource(s) that will be deleted (name, type, project) 2. Ask the user to confirm: "Type 'yes' to confirm deletion of '<pg-name>'." 3. Wait for the user to explicitly type 'yes' Do NOT run the delete command until the user explicitly confirms. A silence or ambiguous response is NOT confirmation. ```bash evroc compute placementgroup delete my-pg --force ``` ## Pitfalls - **Disk images — ask, don't guess**: There is no `disk image list` CLI command. For the live, current list, run: ```bash bash scripts/get-disk-images.sh ``` This fetches from the API using your current `evroc login` token. The [`references/cli-reference.md`](references/cli-reference.md#available-disk-images) table is a cache — use it for offline reference or quick browsing only. Always have the user confirm their choice from the live list or cache. Image names use format `<distro>.<version>.1` (dot before version, hyphen within). e.g. `ubuntu-minimal.24-04.1`, not `ubuntu-minimal-24.04.1` — wrong separators silently create a disk that never becomes Ready (`DiskImageInvalid`). - **Checking readiness — use JSON + jq, not YAML grep**: The `status:` key appears at both the top level and nested under `conditions[]` in the YAML/JSON output. Grepping for `status:` matches the wrong field and returns the condition's status (`"True"`) instead of the condition's type (`"Ready"`). Always use: `evroc compute <resource> get <name> -o json | jq -r '.status.conditions[] | select(.type=="Ready") | .status'` to check readiness. For VM running state, use `.status.virtualMachineStatus`. - **Wait for disk Ready before VM create**: A disk that is not yet Ready will cause VM creation to fail with "disk is not ready". Always poll the disk's Ready condition first. - **Security groups — check or create before VM creation**: Without explicitly assigned security groups, the VM's firewall drops all traffic. Before creating a VM, check existing groups with `evroc networking securitygroup list`. If the user needs SSH access, ensure `default-allow-ssh` is attached or create a custom group with a TCP 22 ingress rule. If the user needs web traffic, ensure 80/443 are allowed. The VM's `SecurityGroupsReady` condition will show `True` once groups are resolved. - **Application ports need ingress rules**: Default security groups only cover SSH (22), HTTP (80), and HTTPS (443). If the user's application listens on a different port, you must create a custom ingress rule for it before the VM can receive traffic on that port. When the user mentions an application by name, look up its default port(s) (see the table in [Security Groups](#security-groups)) and confirm with the user before creating the rule — do not assume the default port is correct. - **`evroc-user` and custom cloud-init**: When you do not provide custom cloudInit userdata, the platform creates `evroc-user` and `--ssh-authorized-key` attaches to it. When you provide custom cloudInit with a `users:` block, `evroc-user` is NOT created, and the key is NOT automatically attached to your custom users. Embed the key in each custom user's `ssh_authorized_keys` in the cloud-init config, or omit the `users:` block to keep the default `evroc-user`. - **SSH troubleshooting — may be locked out**: If SSH fails, check VM status with `evroc compute vm get <name> -o json`. If you are locked out, use cloud-init `write_files` to set up monitoring scripts that run without SSH, or redeploy with a corrected cloud-init config that attaches the SSH key to the intended user. - **cloud-init runs after `Ready`**: The VM reports `Ready` and `Running` before cloud-init finishes. Package installs, downloads, and service setup in `runcmd` can take several minutes after the VM is `Ready`. If the user expects a service to be reachable, wait and probe the application port rather than relying on the VM's `Ready` condition alone. - **Runtime version compatibility**: If the application has runtime dependencies (Java, Python, Node.js, etc.), verify the runtime version matches the application's requirements before deploying. A mismatch causes the service to crash silently — the VM is `Ready` but the port stays `CLOSED` and there's no obvious error without SSH. Look up the app's minimum runtime version (web search if needed), confirm the correct package name in the OS image, and get user confirmation before writing the cloud-init config (see [Runtime version compatibility](#create-a-vm-with-cloud-init)). - **Post-deployment cloud-init validation:** If the application port is closed even though the VM is `Ready`, first check `cloud-init status` via SSH. If it returns `error`, inspect `/var/log/cloud-init-output.log` for the actual failure (common causes: missing parent directories for `write_files`, failed package installs, or missing systemd units started in `runcmd` before they were created). - **SSH host key change warning**: Every new evroc VM generates a fresh SSH host key pair. If the assigned public IP was previously used for a different VM, your local `~/.ssh/known_hosts` will contain the old key and SSH will refuse to connect with a `REMOTE HOST IDENTIFICATION HAS CHANGED` error. This is normal, not a man-in-the-middle attack. Fix it by removing the stale entry before the next connection: ```bash ssh-keygen -R <vm-public-ip> - **Disk before VM**: Always create the boot disk first. The VM references it by name. - **Boot-from flag**: Every disk attachment must specify `--boot-from` (`true` for boot, `false` for data). - **Subnet naming**: Pattern is `default-<region>-<zone>`. The zone in the subnet name must match `--zone`. - **Flavor format**: `<series>.<size>` — e.g. `a1a.m`, `c1a.l`. Series: `a1a` (general), `c1a` (compute-optimized), `m1a` (memory-optimized). - **Delete order**: Delete VMs before their attached disks. Disks must be detached before deletion. - **Output formats**: `-o yaml` (default for `get`), `-o json`, `-o list` (default for `list`, shows names only). Use `yaml` or `json` when you need full resource details. - **Label selectors**: Kubernetes-style syntax: `key=value`, `key in (val1,val2)`, `!key`. Works with user labels and system labels. Add or remove labels with the `label` subcommand (see [`references/cli-reference.md`](references/cli-reference.md)): `evroc compute vm label <name> --add key=value`, `evroc compute disk label <name> --remove key`. Works on VMs, disks, snapshots, and placement groups. - **Name rules**: Max 63 chars, lowercase alphanumeric with hyphens, starting/ending alphanumeric.

evroc-iam

Use when the user wants to create, list, inspect, or delete evroc projects; grant or revoke role bindings; manage service accounts and their credentials (including rs256-jwt and hmac-sigv4 credential types); enable or disable service accounts; or retrieve an access token. Requires the evroc CLI. Do NOT trigger for compute, networking, storage, or Think model tasks.

# evroc-iam Manage evroc Identity and Access Management through the `evroc iam` CLI. Covers projects, role bindings, service accounts, and credentials. evroc IAM uses a principal-role-permission model: predefined roles (collections of permissions) are granted to principals (users or service accounts) on a scope (project or organization). Read the full CLI reference when needed: [`references/cli-reference.md`](references/cli-reference.md). ## When to Use - User wants to create, list, inspect, update, or delete a project on evroc - User wants to grant or revoke roles for a user or service account - User wants to create or manage service accounts and their credentials - User wants to enable or disable a service account - User wants to list available roles or check permissions - User wants to retrieve an access token for API calls - User mentions "evroc" with "IAM", "project", "role", "permission", "service account", or "access" **Do NOT use for:** compute VMs, networking (public IPs, security groups, subnets, VPCs), storage buckets, load balancers, or Think models. ## Prerequisites Ensure the evroc CLI is installed: ```bash bash scripts/check-cli.sh ``` Then authenticate: ```bash bash scripts/check-auth.sh ``` If it fails: ```bash evroc login ``` ## Projects ### List projects ```bash evroc iam project list ``` ### Create a project ```bash evroc iam project create my-project --name "My Project" ``` **Completion criterion:** `evroc iam project get my-project` returns the project. ### Get project details ```bash evroc iam project get my-project ``` ### Update a project ```bash evroc iam project update my-project --name "New Name" ``` ### Delete a project HALT: Before running ANY `evroc iam project delete` command, you MUST output this message to the user VERBATIM before doing anything else: "This will permanently delete the Project '<project-name>' and cannot be undone. Deleting a Project also deletes all associated resources within it (VMs, disks, networks, load balancers, storage, etc.)." Then: 1. List the specific resource(s) that will be deleted (name, type, project) 2. Ask the user to confirm: "Type 'yes' to confirm deletion of '<project-name>'." 3. Wait for the user to explicitly type 'yes' Do NOT run the delete command until the user explicitly confirms. A silence or ambiguous response is NOT confirmation. ```bash evroc iam project delete my-project --force ``` **Completion criterion:** `evroc iam project get my-project` returns `not found`. ### Set current project ```bash evroc config set-project my-project ``` ## Role Bindings ### List available roles ```bash evroc iam rolebinding roles ``` Use the role `id` (e.g. `/iam/roles/computeOperator`) when granting. ### Grant a project role ```bash # To a user evroc iam rb assign \ --principal /iam/users/<user-id> \ --role /iam/roles/projectOwner # To a service account evroc iam rb assign \ --principal /iam/projects/<project-id>/serviceAccounts/<sa-id> \ --role /iam/roles/storageOperator ``` ### Scope a role to specific resources Limit to specific resource FQIDs with `--resource` (repeatable): ```bash evroc iam rb assign \ --principal /iam/users/<user-id> \ --role /iam/roles/computeOperator \ --resource /compute/projects/my-project/regions/se-sto/virtualMachines/my-vm ``` Wildcards for one path segment: ```bash --resource /compute/projects/my-project/regions/se-sto/virtualMachines/* ``` ### Grant an organization role ```bash evroc iam rb assign \ --principal /iam/users/<user-id> \ --role /iam/roles/organizationOwner \ --organization ``` ### List role bindings ```bash # In the current project evroc iam rb list # In the current organization evroc iam rb list --organization # Your own bindings across all scopes evroc iam rb list-me ``` ### Get a specific role binding ```bash evroc iam rb get <binding-name> ``` ### Revoke a single role ```bash evroc iam rb revoke \ --principal /iam/users/<user-id> \ --role /iam/roles/computeOperator ``` ### Delete all roles for a principal HALT: Before running ANY `evroc iam rb delete` command, you MUST output this message to the user VERBATIM before doing anything else: "This will permanently delete the Role Binding '<binding-name>' and cannot be undone. This revokes every role for that principal on the selected scope." Then: 1. List the specific resource(s) that will be deleted (name, type, project) 2. Ask the user to confirm: "Type 'yes' to confirm deletion of '<binding-name>'." 3. Wait for the user to explicitly type 'yes' Do NOT run the delete command until the user explicitly confirms. A silence or ambiguous response is NOT confirmation. ```bash evroc iam rb delete <binding-name> ``` This revokes every role for that principal on the selected scope. ### Test permissions via API ```bash TOKEN=$(evroc iam get-access-token) curl -X POST \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "checks": [ { "permission": "compute.virtualMachines.start", "resource": "/compute/projects/my-project/regions/se-sto/virtualMachines/my-vm" } ] }' \ https://api.evroc.com/iam/v1beta1/testPermissions ``` ## Service Accounts A service account is a non-human identity scoped to a single project. It has no access of its own until you grant it a role via a role binding (see above). Service accounts are addressed by fully qualified identifiers (FQIDs): ``` /iam/projects/<project-id>/serviceAccounts/<service-account-id> ``` ### Create a service account ```bash # Enabled by default evroc iam serviceaccount create my-sa --description "CI/CD automation" # Create disabled (can't authenticate until enabled) evroc iam serviceaccount create my-sa --description "CI/CD automation" --enabled=false ``` ### List service accounts ```bash evroc iam serviceaccount list ``` ### Get service account details ```bash evroc iam serviceaccount get my-sa ``` ### Enable or disable a service account A disabled service account cannot authenticate, even if it has credentials and role bindings. ```bash # Disable evroc iam serviceaccount update my-sa --enabled=false # Re-enable evroc iam serviceaccount update my-sa --enabled ``` ### Credentials A service account can't authenticate until it has a credential. Credentials come in two types, each compatible with a different API: | Type | Key mechanism | Compatible with | |---|---|---| | `rs256-jwt` (default) | Asymmetric (RSA + SHA256). Signs a JWT and exchanges it for a short-lived access token via the OAuth 2.0 private key JWT flow. | evroc public API | | `hmac-sigv4` | Symmetric (HMAC + SHA256). Signs requests with an S3 compatible signature. | S3 compatible API | Use `rs256-jwt` for managing compute, storage, IAM, or other evroc public API resources. Use `hmac-sigv4` for reading or writing objects through the S3 compatible API (e.g. with `s3cmd` or `rclone`). If a workload needs both, create one credential of each type. #### Create a credential ```bash # Default type (rs256-jwt), default 1-year expiry evroc iam serviceaccount credential create cred-1 --service-account my-sa # 90-day expiry evroc iam serviceaccount credential create cred-1 --service-account my-sa --expires-in 2160h # HMAC credential for S3 compatible API evroc iam serviceaccount credential create cred-1 --service-account my-sa --type hmac-sigv4 ``` Credential expiration is mandatory: minimum 24 hours, maximum 2 years. You can't change the expiration after the credential is created. For `rs256-jwt` credentials, the access token lifetime is separate from the credential expiration. The default is 300 seconds (5 minutes), with a minimum of 60 seconds and a maximum of 28800 seconds (8 hours). This is set at credential creation. #### List credentials ```bash evroc iam serviceaccount credential list --service-account my-sa ``` #### Revoke a credential Revocation invalidates the credential immediately and preserves the audit record with the reason. HALT: Before running ANY `evroc iam serviceaccount credential revoke` command, you MUST output this message to the user VERBATIM before doing anything else: "This will permanently revoke the Credential '<cred-name>' and cannot be undone. Any services using this credential will immediately lose access." Then: 1. List the specific resource(s) that will be revoked (credential name, service account, project) 2. Ask the user to confirm: "Type 'yes' to confirm revocation of '<cred-name>'." 3. Wait for the user to explicitly type 'yes' Do NOT run the revoke command until the user explicitly confirms. A silence or ambiguous response is NOT confirmation. ```bash evroc iam serviceaccount credential revoke cred-1 --service-account my-sa --reason "Compromised" ``` #### Delete a credential Delete removes the credential resource entirely. Use revoke if you need an audit trail. ```bash evroc iam serviceaccount credential delete cred-1 --service-account my-sa --force ``` ### Using a credential For `rs256-jwt` credentials, the private key (a JSON Web Key) is printed at creation time. Store it where the workload can read it, e.g. as environment variables (the evroc Go SDK reads these): ```bash export EVROC_SERVICE_ACCOUNT_ID="my-sa" export EVROC_SERVICE_ACCOUNT_SECRET="<private-key>" ``` For `hmac-sigv4` credentials, the access key ID and secret access key are returned at creation time. Use them to configure your S3 client. ### Delete a service account HALT: Before running ANY `evroc iam serviceaccount delete` command, you MUST output this message to the user VERBATIM before doing anything else: "This will permanently delete the service account '<sa-name>' and cannot be undone. Deleting a service account also revokes all associated credentials." Then: 1. List the specific service account(s) that will be deleted (name, type, project) 2. Ask the user to confirm: "Type 'yes' to confirm deletion of '<sa-name>'." 3. Wait for the user to explicitly type 'yes' Do NOT run the delete command until the user explicitly confirms. A silence or ambiguous response is NOT confirmation. ```bash evroc iam serviceaccount delete my-sa --force ``` ## Access Tokens ```bash # Get a short-lived bearer token evroc iam get-access-token # Get current user info evroc iam get-user-info ``` ## Key Predefined Roles | Role | Scope | Description | |---|---|---| | `/iam/roles/projectOwner` | project | Full control | | `/iam/roles/projectContributor` | project | Create/manage resources, no access management | | `/iam/roles/computeOperator` | project | Manage compute resources | | `/iam/roles/computeViewer` | project | Read-only compute access | | `/iam/roles/storageOperator` | project | Manage storage buckets | | `/iam/roles/networkingOperator` | project | Manage networking resources | | `/iam/roles/thinkOperator` | project | Manage Think instances and API keys | | `/iam/roles/organizationOwner` | organization | Full org control | | `/iam/roles/organizationViewer` | organization | Read-only org access | | `/iam/roles/organizationAccessAdministrator` | organization | Manage role bindings and users | Run `evroc iam rb roles` for the complete list. ## Pitfalls - **Private key shown only once**: The private key (JWK for `rs256-jwt`, access key ID + secret for `hmac-sigv4`) is returned only at credential creation and can't be viewed again. Capture it immediately and store it securely. - **Credential expiry is immutable**: Min 24 hours, max 2 years. You can't change the expiration after creation — create a new credential instead. - **Access token lifetime is separate from credential expiry**: For `rs256-jwt` credentials, the short-lived access token expires (default 300s, range 60s–28800s) and the workload must request a new one. This is independent of the credential's own expiration. - **Service accounts are project-scoped**: A service account belongs to exactly one project and its FQID includes that project (`/iam/projects/<project-id>/serviceAccounts/<sa-id>`). It can't be shared across projects. - **Revoke vs delete credentials**: Revoke invalidates immediately and keeps the audit record (with `--reason`). Delete removes the resource entirely. Use revoke for suspected compromise, delete for cleanup. - **Principals use FQIDs, not emails**: Role bindings reference users by user ID (e.g. `/iam/users/5a9bb4ea-...`), not email. Service accounts use `/iam/projects/<project-id>/serviceAccounts/<sa-id>`. Find user IDs in the evroc Console. - **Organization roles ≠ project access**: Organization roles govern only org-level operations. Grant project roles for access to project resources. - **Role assignment is idempotent**: Re-running with the same principal+role replaces the resource list. Use `--resource` to scope, or omit it for all resources. - **PermissionSets are deprecated**: Use role bindings. PermissionSets were a binary admin model; role bindings provide fine-grained predefined roles. - **Custom roles not available**: Only predefined roles are supported. Custom roles, deny rules, and condition-based bindings are not yet available. - **Resource name limits**: Max 63 chars, lowercase alphanumeric with hyphens, starting/ending alphanumeric. - **Labeling service accounts**: Service accounts support the `label` subcommand for adding/removing labels: `evroc iam serviceaccount label <name> --add key=value --remove key`. See [`references/cli-reference.md`](references/cli-reference.md) for details. - **MFA is OTP-only**: TOTP is the only supported MFA mechanism. - **Brute force protection**: After 10 failed login attempts, the account is locked for ~10 minutes with no visual indication. - **CLI aliases**: `rb` for `rolebinding`, `sa` for `serviceaccount`, `ls` for `list`, `rm`/`del` for `delete`, `new` for `create`.

evroc-loadbalancer

Use when the user wants to create, configure, or delete evroc load balancers, backend pools, backend services, health checks, L4 routes, or listeners. Requires the evroc CLI and a pre-existing public IP (managed via evroc-networking). Do NOT trigger for compute VMs, IAM, storage buckets, networking resources, or Think models.

# evroc-loadbalancer Manage evroc load balancers through the `evroc loadbalancer` CLI. Covers backend pools, backend services, health checks, L4 routes, and listeners. Read the full CLI reference when needed: [`references/cli-reference.md`](references/cli-reference.md). ## When to Use - User wants to create or manage a load balancer on evroc - User wants to configure backend pools with VM backends - User wants to set up health checks (HTTP or TCP) for backend services - User wants to configure L4 routes and listeners for traffic distribution - User mentions "evroc" with "load balancer", "LB", "backend pool", or "health check" **Do NOT use for:** compute VMs, networking (public IPs, security groups, subnets, VPCs), IAM, storage, or Think models. ## Prerequisites Ensure the evroc CLI is installed: ```bash bash scripts/check-cli.sh ``` Then authenticate: ```bash bash scripts/check-auth.sh ``` A public IP must exist before creating a load balancer. Create it via **evroc-networking**: ```bash evroc networking publicip create my-lb-ip ``` Note the subnet name when attaching the LB (managed via evroc-networking; defaults to `default-<region>-<zone>`, e.g. `default-se-sto-a`). ## Load Balancer Lifecycle Hierarchy: load balancer → listeners → L4 routes → backend services → backend pools → backends (VMs). ### 1. Create backend pool ```bash evroc loadbalancer backendpool create my-pool \ --backend-ref my-vm-1 \ --backend-ref my-vm-2 ``` ### 2. Create backend service ```bash evroc loadbalancer backendservice create my-service \ --backend-pool-ref my-pool \ --port 8080 \ --ip-protocol-selection IPv4 ``` ### 3. Set health check HTTP: ```bash evroc loadbalancer backendservice sethealthcheckhttp my-service \ --path /health \ --target-port 8080 \ --interval 10s \ --timeout 5s \ --healthy-threshold 2 \ --unhealthy-threshold 3 \ --expected-statuses 200 \ --method GET ``` TCP: ```bash evroc loadbalancer backendservice sethealthchecktcp my-service \ --target-port 8080 \ --interval 10s \ --timeout 5s \ --healthy-threshold 2 \ --unhealthy-threshold 3 ``` ### 4. Create L4 route ```bash evroc loadbalancer l4route create my-route \ --backend-service-ref my-service ``` ### 5. Create load balancer ```bash evroc loadbalancer loadbalancer create my-lb \ --public-ip-ref my-lb-ip ``` ### 6. Add listener ```bash evroc loadbalancer loadbalancer addlistener my-lb \ --listener-name my-listener \ --listener-port 443 \ --listener-protocol TCP \ --listener-route-ref my-route ``` **Completion criterion:** `evroc loadbalancer loadbalancer get my-lb -o yaml | grep -E '^\s+listeners:'` shows the listener attached. ## List and Inspect ```bash # List all evroc loadbalancer loadbalancer list # With details evroc loadbalancer loadbalancer list -o yaml # Get specific evroc loadbalancer loadbalancer get my-lb -o yaml # List related resources evroc loadbalancer backendpool list evroc loadbalancer backendservice list evroc loadbalancer l4route list ``` ## Remove a Listener ```bash evroc loadbalancer loadbalancer removelistener my-lb \ --listener-name my-listener ``` ## Delete Resources Delete in reverse order: listener → load balancer → L4 route → backend service → backend pool. HALT: Before running ANY `evroc loadbalancer ... delete` command, you MUST output this message to the user VERBATIM before doing anything else: For load balancers: "This will permanently delete your Load Balancer '<lb-name>' and cannot be undone." For L4 routes: "This will permanently delete your L4 Route '<route-name>' and cannot be undone." For backend services: "This will permanently delete your Backend Service '<service-name>' and cannot be undone." For backend pools: "This will permanently delete your Backend Pool '<pool-name>' and cannot be undone." Then: 1. List ALL specific resource(s) that will be deleted (name, type, project) 2. Ask the user to confirm: "Type 'yes' to confirm deletion of the listed resources." 3. Wait for the user to explicitly type 'yes' Do NOT run any delete command until the user explicitly confirms. A silence or ambiguous response is NOT confirmation. ```bash evroc loadbalancer loadbalancer delete my-lb --force evroc loadbalancer l4route delete my-route --force evroc loadbalancer backendservice delete my-service --force evroc loadbalancer backendpool delete my-pool --force ``` ## Pitfalls - **Public IP required**: A load balancer requires a pre-existing public IP referenced by `--public-ip-ref`. Create it via **evroc-networking**. - **Hierarchy order**: Create bottom-up (pool → service → route → load balancer → listener). Delete top-down. - **Listener protocol**: Currently only `TCP` is supported for listener protocol. - **Health check target-port**: The `--target-port` on a health check is the port on the backend VM, not the listener port. These can differ. - **Backend references**: `--backend-ref` on a backend pool refers to VM names. Repeatable for multiple VMs. - **Beta service**: The load balancer service is in beta. Command syntax and resource semantics may change between releases. - **Labeling**: All load balancer resources support the `label` subcommand for adding/removing labels: `evroc loadbalancer <resource> label <name> --add key=value --remove key`. See [`references/cli-reference.md`](references/cli-reference.md) for details. - **CLI aliases**: `lb` for `loadbalancer`, `bp` for `backendpool`, `bs` for `backendservice`, `l4r` for `l4route`, `ls` for `list`, `rm`/`del` for `delete`, `new` for `create`.

evroc-networking

Use when the user wants to create, list, inspect, update, or delete evroc networking resources — public IPv4 and IPv6 addresses, security groups (dual-stack), subnets, or VPCs. Requires the evroc CLI. Do NOT trigger for compute VMs, disks, IAM, storage buckets, load balancers, or Think models.

# evroc-networking Manage evroc networking resources through the `evroc networking` CLI. Covers public IPs, security groups, subnets, and VPCs. These resources are **prerequisites** for compute (VMs need subnets and security groups), load balancers (need public IPs), and Rancher deployments (need public IPs and security groups). Create networking resources first, then reference them in other skills. Read the full CLI reference when needed: [`references/cli-reference.md`](references/cli-reference.md). ## When to Use - User wants to create, list, inspect, or delete an evroc public IP - User wants to create or manage security groups and their rules - User wants to create or manage subnets - User wants to create or manage VPCs - User mentions "evroc" with "public IP", "security group", "subnet", "VPC", or "network" **Do NOT use for:** compute VMs, IAM, storage (buckets), load balancers, or Think models. ## Prerequisites Ensure the evroc CLI is installed: ```bash bash scripts/check-cli.sh ``` Then authenticate: ```bash bash scripts/check-auth.sh ``` If it fails: ```bash evroc login ``` ## Public IPs Public IPv4 addresses are a scarce resource. evroc accounts have a 3-address quota. Elastic IPv4s are required for load balancers, Rancher server nodes, and any VM that needs fixed inbound internet access over IPv4. IPv6 is end-to-end by design: each VM in an IPv6-enabled subnet receives a globally routable address without NAT, because every IPv6 subnet allocation is a /64 and individual addresses are globally unique by default (see RFC 4291, Section 2.5.1 on Global Unicast addresses). This eliminates the need for elastic IP allocation, NAT gateways, or port forwarding that IPv4 requires. ### List public IPs ```bash evroc networking publicip list evroc networking publicip list -o yaml ``` ### Get public IP details ```bash evroc networking publicip get <ip-name> -o yaml ``` ### Create a public IP ```bash evroc networking publicip create my-ip ``` **Completion criterion:** `evroc networking publicip get my-ip` returns the IP with an assigned address. ### Delete a public IP HALT: Before running ANY `evroc networking publicip delete` command, you MUST output this message to the user VERBATIM before doing anything else: "This will permanently delete your Public IP '<ip-name>' and cannot be undone." Then: 1. List the specific resource(s) that will be deleted (name, type, project) 2. Ask the user to confirm: "Type 'yes' to confirm deletion of '<ip-name>'." 3. Wait for the user to explicitly type 'yes' Do NOT run the delete command until the user explicitly confirms. A silence or ambiguous response is NOT confirmation. ```bash evroc networking publicip delete <ip-name> --force ``` **Completion criterion:** `evroc networking publicip get <ip-name>` returns `not found`. ## Security Groups Security groups are stateful firewalls attached to VM network interfaces. The default deny model means any traffic not explicitly allowed is blocked. ### List security groups ```bash evroc networking securitygroup list evroc networking securitygroup list -o yaml ``` ### Get security group details ```bash evroc networking securitygroup get <sg-name> -o yaml ``` ### Create a security group ```bash evroc networking securitygroup create my-sg \ --vpc=default-se-sto ``` **Flags:** - `--vpc <vpc-name>`: VPC the security group belongs to (e.g. `default-se-sto`). **Completion criterion:** `evroc networking securitygroup get my-sg` returns the group. ### Add a rule **IPv4 / IPv6 side-by-side:** ```bash # IPv4 rule evroc networking securitygroup addrule my-sg \ --name=allow-ssh \ --direction=Ingress \ --protocol=TCP \ --port=22 \ --remote-ip-or-cidr=0.0.0.0/0 # IPv6 rule evroc networking securitygroup addrule my-sg \ --name=allow-ssh-v6 \ --direction=Ingress \ --protocol=TCP \ --port=22 \ --remote-ip-or-cidr=::/0 ``` **Flags:** - `--port <int>`: Single port for the rule (used as both start and end port). - `--end-port <int>`: End of a port range; use with `--port` (as start) to open a range (e.g. `--port=8000 --end-port=8100`). - `--security-group <sg-name>`: Target security group by name (alternative to passing it as a positional argument). - `--subnet <subnet-name>`: Restrict the rule to traffic from a specific subnet instead of a CIDR. ### Remove a rule ```bash evroc networking securitygroup removerule my-sg \ --name=allow-ssh ``` ### Delete a security group HALT: Before running ANY `evroc networking securitygroup delete` command, you MUST output this message to the user VERBATIM before doing anything else: "This will permanently delete your Security Group '<sg-name>' and cannot be undone." Then: 1. List the specific resource(s) that will be deleted (name, type, project) 2. Ask the user to confirm: "Type 'yes' to confirm deletion of '<sg-name>'." 3. Wait for the user to explicitly type 'yes' Do NOT run the delete command until the user explicitly confirms. A silence or ambiguous response is NOT confirmation. ```bash evroc networking securitygroup delete <sg-name> --force ``` **Completion criterion:** `evroc networking securitygroup get <sg-name>` returns `not found`. ## Subnets Subnets are scoped to a region-zone pair. The default subnet follows the pattern `default-<region>-<zone>` (e.g. `default-se-sto-a`). ### List subnets ```bash evroc networking subnet list evroc networking subnet list -o yaml ``` ### Get subnet details ```bash evroc networking subnet get <subnet-name> -o yaml ``` ### Create a subnet ```bash # Dual-stack subnet (IPv4 + IPv6) evroc networking subnet create my-subnet-dual \ --zone=a \ --ipv4-cidr-block=10.0.1.0/24 \ --stack-type=dual-stack \ --vpc=default-se-sto ``` **CIDR notes:** - **IPv4:** `--ipv4-cidr-block=10.0.1.0/24` (RFC1918 private range, 256 addresses) - **IPv6:** IPv6 addressing is automatic in dual-stack mode; no separate IPv6 CIDR flag is needed. - **VPC:** `--vpc=default-se-sto` attaches the subnet to the default VPC for the `se-sto` region. ### Delete a subnet HALT: Before running ANY `evroc networking subnet delete` command, you MUST output this message to the user VERBATIM before doing anything else: "This will permanently delete your Subnet '<subnet-name>' and cannot be undone." Then: 1. List the specific resource(s) that will be deleted (name, type, project) 2. Ask the user to confirm: "Type 'yes' to confirm deletion of '<subnet-name>'." 3. Wait for the user to explicitly type 'yes' Do NOT run the delete command until the user explicitly confirms. A silence or ambiguous response is NOT confirmation. ```bash evroc networking subnet delete <subnet-name> --force ``` **Completion criterion:** `evroc networking subnet get <subnet-name>` returns `not found`. ## VPCs VPCs are isolated network containers. A VPC can span multiple regions. Subnets live inside VPCs. ### List VPCs ```bash evroc networking vpc list evroc networking vpc list -o yaml ``` ### Get VPC details ```bash evroc networking vpc get <vpc-name> -o yaml ``` ### Create a VPC ```bash # Dual-stack VPC (IPv4 + IPv6) evroc networking vpc create my-vpc-dual \ --ipv4-cidr-block=10.0.0.0/16 \ --stack-type=dual-stack ``` **CIDR notes:** - **IPv4:** `--ipv4-cidr-block=10.0.0.0/16` (RFC1918, ~65k addresses across all subnets) - **IPv6:** IPv6 addressing is automatic in dual-stack mode; no separate IPv6 CIDR flag is needed. ### Delete a VPC HALT: Before running ANY `evroc networking vpc delete` command, you MUST output this message to the user VERBATIM before doing anything else: "This will permanently delete your VPC '<vpc-name>' and cannot be undone." Then: 1. List the specific resource(s) that will be deleted (name, type, project) 2. Ask the user to confirm: "Type 'yes' to confirm deletion of '<vpc-name>'." 3. Wait for the user to explicitly type 'yes' Do NOT run the delete command until the user explicitly confirms. A silence or ambiguous response is NOT confirmation. ```bash evroc networking vpc delete <vpc-name> --force ``` **Completion criterion:** `evroc networking vpc get <vpc-name>` returns `not found`. ## Quota Check Helper (IPv4 Elastic IPs) Before creating resources that need public IPv4 addresses (load balancers, VMs with fixed IPv4), verify remaining quota: ```bash bash scripts/check-public-ip-quota.sh <needed> ``` Returns the number of available elastic IPv4 addresses vs the 3-address account quota. Exits non-zero if insufficient. **Not needed for IPv6.** Every VM in an IPv6-enabled subnet receives a public address automatically without quota tracking. ## Pitfalls - **Default-deny model**: evroc security groups block all traffic not explicitly allowed. If a VM can't be reached, check the security group rules (not just the VM). - **Subnet naming**: Default subnets follow `default-<region>-<zone>` (e.g. `default-se-sto-a`). Custom subnets follow the same zone-scoping rules. - **Security group scope**: Security groups are project-scoped. A security group in one project cannot be attached to a VM in another project. - **Rule direction**: `Ingress` = inbound to the VM. `Egress` = outbound from the VM. The default-deny applies to both directions independently. - **IPv4 quota**: evroc accounts have a 3-address elastic IPv4 quota. IPv6 has no per-address quota. Run `evroc networking publicip list` to check IPv4 usage. Free unused IPv4 addresses before creating new ones. - **IPv6 security groups require ICMP**: In dual-stack or IPv6-only subnets, security group rules must explicitly allow ICMP traffic. The CLI `--protocol ICMP` value covers both ICMPv4 and ICMPv6 depending on the CIDR family. IPv6 Neighbor Discovery (N/D), Router Solicitation (RS), and Router Advertisement (RA) all use ICMPv6. Blocking this prevents address configuration and basic Layer 3 reachability. Add a rule for `ICMP` on both Ingress and Egress directions. - **Delete order**: Delete VMs and load balancers before deleting the security groups, public IPs, and subnets they reference. Resources with active references cannot be deleted. - **Name rules**: Max 63 chars, lowercase alphanumeric with hyphens, starting/ending alphanumeric. - **Labeling**: All networking resources (public IPs, security groups, subnets, VPCs) support the `label` subcommand for adding/removing labels: `evroc networking <resource> label <name> --add key=value --remove key`. See [`references/cli-reference.md`](references/cli-reference.md) for details. - **CLI aliases**: `sg` for `securitygroup`, `vpc` for `virtualprivatecloud`, `ls` for `list`, `rm`/`del` for `delete`, `new` for `create`.

evroc-rancher

Use when the user wants to deploy, inspect, or destroy Rancher Manager on evroc cloud using Terraform. Covers K3s provisioning, cloud-init automation, and full lifecycle management. Do NOT trigger for bare VM creation (see evroc-compute), networking resources (see evroc-networking), IAM tasks (evroc-iam), or Think model deployment (evroc-think).

# evroc-rancher Deploy Rancher Manager on evroc cloud. Provisions a Rancher server node (K3s + cert-manager + Rancher) and downstream workload nodes (K3s agents) via the evroc Terraform provider. Cloud-init automates the full software stack. For troubleshooting, read: [`references/troubleshooting.md`](references/troubleshooting.md). ## When to Use - User wants to deploy Rancher on evroc - User wants to destroy an evroc Rancher deployment - User wants to check the status of a Rancher deployment on evroc - User mentions "rancher" and "evroc" together in a deployment context **Do NOT use for:** bare VM creation without Rancher, networking (public IPs, security groups, subnets, VPCs), IAM tasks, storage buckets, or Think models. ## Prerequisites Run the prerequisite checker: ```bash bash scripts/prerequisites.sh ``` If any prerequisite needs manual action, the script exits non-zero and reports what the user must do. **Completion criterion:** Script exits 0 with "✓ All prerequisites met." **Note:** On first run, `terraform init` downloads the evroc Terraform provider (~20 MB) from `registry.terraform.io`. This requires network access. Subsequent runs use the local plugin cache. ## Operations Three operations, each with a dedicated script. Collect required inputs from the user before running any script. ### Deploy **Required inputs** (collect via `clarify` if not provided): 1. **SSH public key** — If `prerequisites.sh` found one, confirm it. Otherwise ask the user to paste it or generate one with `ssh-keygen -t ed25519`. 2. **Rancher admin password** — Must be at least 14 characters. 3. **Number of workload nodes** — If not specified, ask via `clarify`. Default: 1. Each needs a public IP (see IP quota pitfall). **Optional inputs** (offer defaults): | Parameter | Default | |-----------|---------| | evroc region | `se-sto` | | evroc zone | `a` | | resource prefix | `rancher` | **Deploy workflow** (copy this checklist and track progress): ``` [ ] 1. Prerequisites pass (prerequisites.sh exits 0) [ ] 2. Inputs collected (SSH key, password, node count) [ ] 3. Run deploy.sh [ ] 4. terraform apply exits 0 [ ] 5. VMs provisioned (~5–8 min) [ ] 6. cloud-init completes (~5–10 min) [ ] 7. Rancher URL returns 200 [ ] 8. Report URL + credentials to user ``` Run: ```bash bash scripts/deploy.sh \ <workdir> \ "<ssh_public_key>" \ "<rancher_password>" \ [region] [zone] [prefix] [workload_node_count] ``` **Completion criterion:** `terraform apply` exits 0 and outputs the Rancher URL. Report the URL, node IPs, and admin password to the user. ### Status ```bash bash scripts/status.sh <rancher_node_ip> ``` Checks: cloud-init status, K3s nodes, Rancher pods, cert-manager pods. **Completion criterion:** All checks show Ready/Running state. If any fail, read [`references/troubleshooting.md`](references/troubleshooting.md). ### Destroy HALT: Before running `bash scripts/destroy.sh`, you MUST output this message to the user VERBATIM before doing anything else: "This will permanently destroy the entire Rancher deployment and all associated resources (VMs, disks, public IPs, security groups, networks) and cannot be undone." Then: 1. List ALL specific resources that will be destroyed (VM names, disk names, public IPs, security groups, networks — everything Terraform manages) 2. Ask the user to confirm: "Type 'yes' to confirm destruction of the entire deployment." 3. Wait for the user to explicitly type 'yes' Do NOT run the destroy command until the user explicitly confirms. A silence or ambiguous response is NOT confirmation. ```bash bash scripts/destroy.sh <workdir> ``` **Completion criterion:** `terraform destroy` exits 0 with "Destroy complete." Confirm all resources are removed. ## Git Init After a successful deploy: ```bash cd <workdir> git init git add -A git commit -m "Rancher evroc deployment" ``` The user manages remotes and pushes themselves. ## Quick Reference | Script | Purpose | Usage | |---|---|---| | `scripts/prerequisites.sh` | Check and install tools | `bash scripts/prerequisites.sh` | | `scripts/deploy.sh` | Provision VMs + Rancher | `bash scripts/deploy.sh <workdir> "<ssh_key>" "<password>" [region] [zone] [prefix] [count]` | | `scripts/destroy.sh` | Tear down everything | `bash scripts/destroy.sh <workdir>` | | `scripts/status.sh` | Check deployment health | `bash scripts/status.sh <rancher_ip>` | ## Assets The `assets/` directory contains the Terraform module files. `deploy.sh` copies them into the work directory at deploy time: | File | Role | |---|---| | `main.tf` | Provider, security groups, VMs, disks, public IPs, outputs | | `variables.tf` | Input variable definitions | | `terraform.tfvars.example` | Example variable file | | `cloud-init-rancher.yaml` | Cloud-init: K3s server + cert-manager + Rancher Helm | | `cloud-init-workload.yaml` | Cloud-init: K3s agent joining the server cluster | ## Pitfalls - **Public IP quota**: evroc accounts have a 3-IP quota. The deployment needs 1 IP for the Rancher server + 1 IP per workload node. Run `bash scripts/check-public-ip-quota.sh <needed>` before deploying (or use the **evroc-networking** skill). If the user hits a 403 quota error, they must free IPs via evroc-networking: `evroc networking publicip list` + `evroc networking publicip delete <id>`. - **Bootstrap time**: Rancher takes 5–10 minutes to become ready after VMs are provisioned. A 404 at the Rancher URL during this window is normal. - **SSH key mismatch**: The SSH key in `terraform.tfvars` must match the key used to SSH into the VMs. - **Password requirements**: The Rancher admin password must be at least 14 characters. Shorter passwords cause the Helm install to fail silently. - **K3s version**: Pinned to `v1.35.7+k3s1` in `variables.tf` for Rancher chart compatibility (requires `kubeVersion < 1.36.0`). Do not override to v1.36.x without also bumping the Rancher chart version. - **Terraform provider**: The `.terraform/` directory is NOT committed. On first deploy, `terraform init` downloads the evroc provider from `registry.terraform.io`. Requires internet access for the first run.

evroc-storage

Use when the user wants to create, list, inspect, or delete evroc storage resources — S3-compatible buckets, file stores (NFS), or bucket service accounts. Requires the evroc CLI. Do NOT trigger for compute VMs, IAM, networking, load balancers, or Think models.

# evroc-storage Manage evroc storage resources through the `evroc storage` CLI. Covers S3-compatible buckets, file stores (NFS), and bucket service accounts. Read the full CLI reference when needed: [`references/cli-reference.md`](references/cli-reference.md). ## When to Use - User wants to create, list, inspect, or delete an S3-compatible bucket - User wants to copy files to or from a bucket - User wants to get S3 credentials for programmatic access - User wants to create or manage a file store (NFS) - User wants to create a bucket service account for bucket access - User mentions "evroc" with "bucket", "storage", "S3", "file store", or "object storage" **Do NOT use for:** compute VMs, networking (public IPs, security groups, subnets, VPCs), IAM, load balancers, or Think models. ## Prerequisites Ensure the evroc CLI is installed: ```bash bash scripts/check-cli.sh ``` Then authenticate: ```bash bash scripts/check-auth.sh ``` If it fails: ```bash evroc login ``` ## Object Storage (Buckets) ### List buckets ```bash evroc storage bucket list evroc storage bucket list -o yaml evroc storage bucket list -l "environment=production" ``` ### Get bucket details ```bash evroc storage bucket get <bucket-name> -o yaml ``` ### Create a bucket ```bash # Simple evroc storage bucket create my-bucket # With object versioning evroc storage bucket create my-bucket --object-retention-mode=Versioned # With object locking evroc storage bucket create my-bucket \ --object-retention-mode=Locking \ --default-object-lock-mode=Soft \ --default-object-lock-duration-days=30 # With a lifecycle policy evroc storage bucket create my-bucket \ --lifecycle-policy=file://lifecycle-policy.json ``` ### Get S3 credentials Before copying objects or using S3 clients, retrieve credentials: ```bash evroc storage bucket get-s3-credentials # Custom path evroc storage bucket get-s3-credentials --path /tmp/s3-creds.ini ``` Credentials are written to `~/.evroc/credentials.ini` by default. ### Copy objects ```bash # Upload evroc storage bucket copy --from /path/to/local/file --to bucket://my-bucket/object-name # Download evroc storage bucket copy --from bucket://my-bucket/object-name --to /path/to/local/file # Between buckets evroc storage bucket copy \ --from bucket://source-bucket/object \ --to bucket://dest-bucket/object ``` ### List objects ```bash # Current objects evroc storage bucket list # Include deleted versions evroc storage bucket list --include-deleted # Include all versions evroc storage bucket list --include-all ``` ### Delete a bucket or objects HALT: Before running ANY `evroc storage bucket delete` command, you MUST output this message to the user VERBATIM before doing anything else: "This will permanently delete your Bucket '<bucket-name>' and cannot be undone." If using `--purge`, also output: "All objects within the bucket will also be deleted." Then: 1. List the specific resource(s) that will be deleted (name, type, project) 2. Ask the user to confirm: "Type 'yes' to confirm deletion of '<bucket-name>'." 3. Wait for the user to explicitly type 'yes' Do NOT run the delete command until the user explicitly confirms. A silence or ambiguous response is NOT confirmation. ```bash # Empty bucket evroc storage bucket delete my-bucket --force # Non-empty bucket (purges all contents) evroc storage bucket delete my-bucket --purge --force # Specific object evroc storage bucket delete bucket://my-bucket/example.txt # All objects, keep bucket evroc storage bucket delete bucket://my-bucket/* --force ``` **Completion criterion:** `evroc storage bucket get my-bucket` returns `not found`. ## File Stores (NFS) ### Create a file store ```bash evroc storage filestore create my-store --zone a ``` ### List file stores ```bash evroc storage filestore list evroc storage filestore list -o yaml ``` ### Get file store details ```bash evroc storage filestore get <filestore-name> -o yaml ``` ### Delete a file store HALT: Before running ANY `evroc storage filestore delete` command, you MUST output this message to the user VERBATIM before doing anything else: "This will permanently delete your File Store '<filestore-name>' and cannot be undone." Then: 1. List the specific resource(s) that will be deleted (name, type, project) 2. Ask the user to confirm: "Type 'yes' to confirm deletion of '<filestore-name>'." 3. Wait for the user to explicitly type 'yes' Do NOT run the delete command until the user explicitly confirms. A silence or ambiguous response is NOT confirmation. ```bash evroc storage filestore delete my-store --force ``` ## Bucket Service Accounts ### Create a bucket service account ```bash evroc storage bucketserviceaccount create my-sa \ --bucket my-bucket \ --bucket my-other-bucket ``` ### List bucket service accounts ```bash evroc storage bucketserviceaccount list ``` ### Get details ```bash evroc storage bucketserviceaccount get <service-account-name> ``` ### Delete HALT: Before running ANY `evroc storage bucketserviceaccount delete` command, you MUST output this message to the user VERBATIM before doing anything else: "This will permanently delete your Bucket Service Account '<sa-name>' and cannot be undone." Then: 1. List the specific resource(s) that will be deleted (name, type, project) 2. Ask the user to confirm: "Type 'yes' to confirm deletion of '<sa-name>'." 3. Wait for the user to explicitly type 'yes' Do NOT run the delete command until the user explicitly confirms. A silence or ambiguous response is NOT confirmation. ```bash evroc storage bucketserviceaccount delete my-sa --force ``` ## Pitfalls - **S3 credentials required for copy**: `bucket copy` and `bucket list` (within buckets) read S3 credentials from `~/.evroc/credentials.ini`. Run `evroc storage bucket get-s3-credentials` first if you haven't already. - **Bucket must be empty to delete**: Non-empty buckets require `--purge` to delete. If object locking is enabled, `--purge` is ignored — delete all versions first. - **Object retention modes**: `Disabled` (default), `Suspended`, `Versioned`, `Locking`. Object lock requires `Locking` mode and a default lock mode+duration. - **File store requires zone**: `--zone` is required for file store creation. The file store uses NFS v4.1. - **Bucket service accounts**: Must reference at least one bucket via `--bucket` (repeatable for multiple buckets). - **Labeling**: Buckets, file stores, and bucket service accounts support the `label` subcommand for adding/removing labels: `evroc storage <resource> label <name> --add key=value --remove key`. See [`references/cli-reference.md`](references/cli-reference.md) for details. - **CLI aliases**: `bu` for `bucket`, `fs` for `filestore`, `bsa` for `bucketserviceaccount`, `ls` for `list`, `rm`/`del` for `delete`, `new` for `create`.

evroc-think

Use when the user wants to deploy, start, stop, update, or delete evroc Think dedicated GPU model instances; list or use shared OpenAI-compatible models; create or manage Think API keys; or launch coding agents with Think as backend. Requires the evroc CLI. Do NOT trigger for bare compute VMs (evroc-compute), IAM tasks (evroc-iam), or non-evroc model providers.

# evroc-think Manage evroc Think Models — a Model-as-a-Service inference platform hosting leading open-source AI models on NVIDIA H100 and B200 GPUs. Two modes of operation: 1. **Shared Models** — managed by evroc, ready to use, OpenAI-compatible endpoints. 2. **Dedicated Model Instances** — isolated GPU resources for a single model. Read the full CLI reference when needed: [`references/cli-reference.md`](references/cli-reference.md). ## When to Use - User wants to create, list, inspect, start, stop, or delete a model instance - User wants to list available models or instance sizes - User wants to list or use shared models - User wants to create or manage API keys for Think endpoints - User wants to launch a coding agent (Claude Code, Codex, Hermes, OpenCode, Aider) with Think as backend - User mentions "evroc" with "Think", "model", "inference", "GPU", "LLM", or "AI model" **Do NOT use for:** compute VMs, networking (public IPs, security groups, subnets, VPCs), IAM, storage, load balancers, or non-evroc model providers. ## Prerequisites Ensure the evroc CLI is installed: ```bash bash scripts/check-cli.sh ``` Then authenticate: ```bash bash scripts/check-auth.sh ``` If it fails: ```bash evroc login ``` ## Shared Models Shared Models are managed by evroc — no deployment needed. All shared models expose OpenAI API-compatible endpoints. ### List shared models ```bash evroc think sharedmodel list ``` ### Use a shared model (curl example) ```bash TOKEN=$(evroc think apikey create temp-key) curl -H "Authorization: Bearer ${TOKEN}" \ https://models.think.evroc.com/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{"model":"<shared-model-id>","messages":[{"role":"user","content":"Hello"}]}' ``` ## Dedicated Model Instances For more details, read: [`references/dedicated-instances.md`](references/dedicated-instances.md) ### List available models ```bash evroc think model list ``` ### Get model details ```bash evroc think model get <model-id> -o yaml ``` ### List instance sizes ```bash evroc think size list ``` Sizes encode GPU type + resources, e.g. `1-b200-27c-240g` (1× B200, 27 vCPUs, 240GB RAM). ### Create a dedicated instance ```bash evroc think instance create my-instance \ --model openai-gpt-oss-120b \ --size 1-b200-27c-240g ``` - `--model` — Required. Model ID from `evroc think model list` (dashed format). - `--size` — Required. Instance size ID from `evroc think size list`. - `--stopped` — Create in stopped state (default: running). **Completion criterion:** `evroc think instance get my-instance -o yaml | grep -E '^\s+status:'` shows `Running`. ### List instances ```bash evroc think instance list ``` ### Get instance details ```bash evroc think instance get my-instance -o yaml ``` ### Start / stop ```bash evroc think instance start my-instance evroc think instance stop my-instance ``` ### Update a model ```bash evroc think instance update my-instance --model <new-model-id> ``` ### Delete an instance HALT: Before running ANY `evroc think instance delete` command, you MUST output this message to the user VERBATIM before doing anything else: "This will permanently delete the Model Instance '<instance-name>' and cannot be undone." Then: 1. List the specific resource(s) that will be deleted (name, type, project) 2. Ask the user to confirm: "Type 'yes' to confirm deletion of '<instance-name>'." 3. Wait for the user to explicitly type 'yes' Do NOT run the delete command until the user explicitly confirms. A silence or ambiguous response is NOT confirmation. ```bash evroc think instance delete my-instance --force ``` **Completion criterion:** `evroc think instance get my-instance` returns `not found`. ## API Keys API keys authenticate requests to Think endpoints (both shared and dedicated). ### Create an API key ```bash # No expiry evroc think apikey create my-key # With expiry evroc think apikey create my-key --expiry-timestamp 2026-12-31T23:59:59Z ``` ### List API keys ```bash evroc think apikey list ``` ### Delete an API key HALT: Before running ANY `evroc think apikey delete` command, you MUST output this message to the user VERBATIM before doing anything else: "This will permanently delete your API Key '<key-name>' and cannot be undone. Any services using this key will immediately lose access." Then: 1. List the specific resource(s) that will be deleted (name, type, project) 2. Ask the user to confirm: "Type 'yes' to confirm deletion of '<key-name>'." 3. Wait for the user to explicitly type 'yes' Do NOT run the delete command until the user explicitly confirms. A silence or ambiguous response is NOT confirmation. ```bash evroc think apikey delete my-key --force ``` ## Coding Agent Integration Think can serve as the model backend for coding agents. ### One-off session (no config files written) ```bash # Claude Code evroc think code claude # Codex evroc think code codex # Hermes Agent evroc think code hermes # OpenCode evroc think code opencode # Aider evroc think code aider ``` This sets `OPENAI_API_KEY` and `OPENAI_BASE_URL` for the session and spawns the agent. An API key is created automatically if you're logged in. A project is created automatically if you don't have one. ### Permanent project config (committable) Write agent-specific config files into your project directory: ```bash # Claude Code → CLAUDE.md + .claude/settings.local.json evroc think code init claude # Codex → ~/.codex/config.toml evroc think code init codex # Hermes Agent → ~/.hermes/config.yaml evroc think code init hermes # OpenCode → opencode.jsonc evroc think code init opencode ``` ## Inference API Think exposes `https://models.think.evroc.com/v1` with OpenAI-compatible endpoints. ### List models via API ```bash TOKEN=$(evroc think apikey create temp-key) curl -s -H "Authorization: Bearer ${TOKEN}" \ https://models.think.evroc.com/v1/models | python3 -c \ "import sys,json; [print(m['id']) for m in json.load(sys.stdin)['data']]" ``` ## Pitfalls - **Dedicated vs shared**: Shared models are zero-deployment. Dedicated instances require choosing a model and size, then waiting for deployment. - **Model IDs use dashed format** matching `evroc think model list` output: e.g. `openai-gpt-oss-120b`, not HuggingFace slash format. - **Size format**: Encodes GPU + resources, e.g. `1-b200-27c-240g`. Run `evroc think size list` for the authoritative list. - **API key expiry**: Keys created without `--expiry-timestamp` have no expiry. Set an expiry for short-lived access. - **Code integration auto-creates resources**: `evroc think code` and `evroc think code init` auto-create an API key and project if missing. Check `evroc think apikey list` after to see what was created. - **OpenAI API compatibility**: Endpoints follow OpenAI format. Any OpenAI-compatible client works. - **CLI aliases**: `mi` for `modelinstance`/`instance`, `ls` for `list`, `rm`/`del` for `delete`, `new` for `create`.