npx agentspec-cli install 1358dd5b-54dd-4760-bde9-4f54b3d5b3ca --file-type claude-md→ .claude/rules/
# tofu-proxmox — Agent Guide
> **⛔ NO DIRECT APPLIES TO ANY DEVICE — EVER.**
>
> Direct changes to **any** device — router, firewall, switch, access point, hypervisor, mail gateway, or any other appliance — are **NEVER** permitted, by anyone, for any reason. This bans hand-run `tofu apply`, hand-run `ansible-playbook`, SSH/serial/CLI config writes, REST/API mutations, and web-GUI/console edits.
>
> **Every change MUST flow through the sanctioned pipeline:** declare intent in **prod-netbox** (the single source of truth), then realize it **only** through **prod-semaphore** (the sanctioned runner). A change that did not go **prod-netbox → prod-semaphore** must never reach a device.
>
> **Sole exception:** a specific direct action is permitted *only* when the operator authorizes that exact action in advance by answering an explicit, **alarm-flavored `AskUserQuestion`** — one that names the device, the precise action, and the risk — **in the affirmative**. No standing grants, no inferred permission, no carrying one approval to another action or device. Absent that in-the-moment "yes," the answer is no.
>
> **Never offload the work onto the operator.** When you are blocked, ask for the break-glass authorization that lets *you* do the job — never ask the operator to run a command, SSH in, or make the change on your behalf. The operator grants permission; they do not perform your labour.
A native OpenTofu/Terraform provider for the Proxmox family (PVE/PBS/PMG/PDM), generic over the
shared `/api2/json` REST API. Sibling of `tofu-aruba-aos` / `tofu-openwrt-ubus`; built to the same
house standards.
General Go / Terraform-provider standards are canonical at
`/home/jameson/source/ai-prompts/go.md` (and `.../tofu.md` for the OpenTofu side). Read those first.
This file holds only repo-specific facts.
## Design
- **`proxmox_object`** (declarative): generalization of `arubaos_object`. `path` + declared-JSON
`body` with `subsetMatches`/`subsetSuppress` → manage-declared-only, import-to-0-diff. `create_path`,
`delete_method` (DELETE|PUT|NONE), `delete_body`. For **synchronous** config endpoints only.
- **`proxmox_task`** (imperative): generalization of `openwrt_ubus_call`, plus UPID polling. Issues
`method`+`path`+`params`; if the response is a UPID, `TaskWait` polls the task to terminal status.
`triggers` re-invoke; optional `destroy_{method,path,params}` runs an inverse op on destroy. For
**async** lifecycle ops (create/start/migrate/destroy guests, backups).
- **`internal/proxmox`** transport (zero terraform imports): `product.go` (per-product port/cookie/
token spec), `client.go` (ticket+CSRF and API-token auth, Get/Post/Put/Delete, APIError/NotFound,
`writeMu`), `task.go` (ParseUPID, TaskStatus, TaskWait).
## Product differences (encoded in `internal/proxmox/product.go`)
| Product | Port | Cookie | API token | Notes |
|---|---|---|---|---|
| pve | 8006 | PVEAuthCookie | `PVEAPIToken=u@r!id=secret` | self-signed cert (insecure=true) |
| pbs | 8007 | PBSAuthCookie | `PBSAPIToken=u@r!id:secret` (colon) | |
| pmg | 8006 | PMGAuthCookie | **none — ticket only** | reject token auth at Configure |
| pdm | 8443 | `__Host-PDMAuthCookie` | `PDMAPIToken=u@r!id:secret` (colon) | verified vs pdm-lab 1.1.1 — NOT the PVE scheme; aggregator/proxy. Token auth works; *ticket* login is cookie-only (PDM returns no body `ticket`), so `client.login()` would need Set-Cookie parsing for password auth — token auth is the supported path. |
## Repo-specific wrinkles
- **Async-everywhere:** PVE writes that matter return UPIDs → use `proxmox_task`, not `proxmox_object`.
- **Key removal:** PVE merges PUT keys; it removes a key via a `delete=` param, not DELETE-on-subpath.
Unset config keys via `delete_body` (`{"delete":"hookscript,mp0"}`) on update. The subset model
never unsets keys you stop declaring — by design. Fix spurious diffs in subset logic, never by
widening stored state.
- **Two transports (decision 2026-06-24):** like `tofu-opnsense` (REST `opnsense_object` +
SSH/PHP `opnsense_system_config`), this provider carries an **API transport** (`proxmox_object`,
the PVE REST API) **and** an **SSH transport** (`proxmox_host_config`, `internal/proxmox/ssh.go`,
key/cert auth — never `sshpass`). The earlier "API-only / host-side lives in Ansible" charter is
retired for the **node's Debian-OS config**: hostname, timezone, DNS, NTP/timesyncd, remote
syslog, logrotate, `sshd_config.d` drop-ins (port/password-auth/AllowUsers/hardening), and
`snmpd.conf` (v2c + v3) are managed natively by `proxmox_host_config` over SSH — replacing the 15
`hv/_common` `scottwinkler/shell` modules (the 60 duplicated sshpass scripts).
- **Genuine host-side boundary (still out of scope):** hookscript files, bind-mount source dirs,
NFS mounts, NVIDIA drivers — these are guest/storage plumbing with neither a PVE API nor a
declarable node-OS config surface; they remain in Ansible/host bootstrap. `proxmox_host_config`
covers node-OS *settings*, not guest/storage plumbing.
- **ai-lab CT fields** (`hookscript`, `mpN`, `netN` bridge+tag, `unprivileged`, raw `lxc.*`) are all
settable via `PUT /nodes/{node}/lxc/{vmid}/config`. VMID = `vlan*1000+octet` is config-layer math.
## Relationship to the lab
The `tofu` monorepo currently manages PVE with the third-party `bpg/proxmox` provider in
`opentofu/hv/pve`. Migrating that to this provider is a **separate, later** effort — do not touch
`hv/pve` from here. Service installs on guests are handled by the sibling `tofu-proxmox-services`
provider (ansible orchestration); NetBox remains the source of truth for guest definitions.
Terminal-2 — read AGENTS.md first
Canonical multi-agent rules: `@AGENTS.md`, then `@PROJECT_BIBLE.md`. Full lockdown: `CLAUDE.md §1–3` (that file wins on any conflict). This `.mdc` is the Cursor-specific pointer.
by AgentHub Crawler
Project Agent Guide (AGENTS.md)
This repository hosts various Tampermonkey user scripts. Follow these rules to keep them consistent.
by AgentHub Crawler
CLAUDE.md
Build, test and box-routing guidance lives in **`AGENTS.md`** — read it. This file is one doctrine that overrides a default you almost certainly arrived with.
by AgentHub Crawler
Repository Guidelines
Sign in to leave a comment.
**NEVER work directly on `main` branch.** All development must happen on feature branches.
by AgentHub Crawler