# freenas-proxmox — Claude Code Guide ## What This Project Is A storage plugin "wedge" for Proxmox VE (PVE) that allows PVE to manage iSCSI LUNs on TrueNAS/FreeNAS via the TrueNAS REST API instead of the traditional SSH-based `iscsiadm` approach. The plugin installs as a Debian package and works by: 1. Deploying a new Perl LunCmd handler (`FreeNAS.pm`) to `/usr/share/perl5/PVE/Storage/LunCmd/` 2. Patching three Proxmox VE system files at install time via `dpkg triggers` ## Repository Layout ``` freenas-proxmox/ ├── perl5/PVE/Storage/ │ ├── Custom/FreeNAS.pm # Old attempt at a full custom storage type (unfinished) │ ├── LunCmd/FreeNAS.pm # MAIN backend: iSCSI LunCmd via TrueNAS REST API │ ├── LunCmd/FreeNAS-ng.pm # Next-gen draft (not in use) │ └── ZFSPlugin-*.pm.patch # Per-PVE-version patches for ZFSPlugin.pm ├── pve-manager/js/ │ └── pvemanagerlib-*.js.patch # Per-PVE-version patches for the Proxmox UI JS ├── pve-docs/api-viewer/ │ └── apidoc-*.js.patch # Per-PVE-version patches for the API docs JS ├── perl5/REST/Client.pm # Bundled REST::Client (also an apt dependency) ├── stable-5/, stable-6/, stable-7/, stable-8/ │ # Per-major-version snapshots of patches + originals └── .github/workflows/action.yml # Currently just dispatches to external packer repo ``` ## How the Current Build Works (Two-Repo Problem) 1. A push to this repo triggers `action.yml` which fires a `repository_dispatch` event to `TheGrandWazoo/freenas-proxmox-packer` 2. That separate repo holds the DEBIAN package structure (`DEBIAN/control`, `postinst`, `postrm`, `triggers`) 3. Its CI builds the `.deb` with `dpkg-deb` and pushes to Cloudsmith The `postinst` script at install time **git-clones this repo** to `/usr/local/src/freenas-proxmox` and applies patches from there. This is the key fragility — internet access required at package install time. ## Three Files That Get Patched at Install Time | File | What the patch adds | |------|---------------------| | `/usr/share/perl5/PVE/Storage/ZFSPlugin.pm` | Adds `freenas` as a valid iSCSI provider, routes `run_lun_command` to `FreeNAS.pm`, adds custom properties/options | | `/usr/share/pve-manager/js/pvemanagerlib.js` | Adds `FreeNAS/TrueNAS API` to the iSCSI provider dropdown; adds UI fields for API host, user, secret, SSL, token auth | | `/usr/share/pve-docs/api-viewer/apidoc.js` | Registers TrueNAS-specific properties in the API docs | ## Authentication Modes Supported - **Basic Auth**: `freenas_user` + `freenas_password` (deprecated but still works) - **Bearer Token**: `truenas_token_auth=true` + `truenas_secret` (preferred for TrueNAS SCALE) ## TrueNAS API Version Detection The plugin auto-detects v1.0 vs v2.0 API based on the TrueNAS version string and HTTP response: - `>= 11.03.01.00` → uses v2.0 API - Older → uses v1.0 API ## Known Issues / Active Work - Versioned patches are fragile — each PVE minor release may need a new patch - `postinst` clones the repo at install time (requires internet, fragile) - `Custom/FreeNAS.pm` is unfinished (duplicate `properties()` and `options()` subs) - REST::Client is both an apt dependency AND manually bundled - Everything is named `FreeNAS` internally but the product is now `TrueNAS` - SSH keys still required for ZFS pool listing (separate Proxmox code path via `ZFSPoolPlugin.pm`) - Max LUN limit bug (#150) ## Key Perl Modules - `PVE::Storage::LunCmd::FreeNAS` — the main plugin. Entry point: `run_lun_command()` - Functions: `freenas_api_connect`, `freenas_api_check`, `freenas_api_call`, `freenas_list_lu`, `run_create_lu`, `run_delete_lu`, `run_modify_lu` ## Packaging / Deployment Notes - Package name: `freenas-proxmox` - Apt repos: Cloudsmith (`ksatechnologies/truenas-proxmox` stable, `ksatechnologies/truenas-proxmox-testing` beta) - Install: `apt install freenas-proxmox` after adding the repo - The package uses dpkg `triggers` to re-apply patches when Proxmox VE packages are upgraded ## ADRs / Plans / Runbooks See `.claude/cos/adrs/` for Architecture Decision Records. See `.claude/cos/plans/` for implementation plans. See `.claude/cos/runbooks/` for operational runbooks. --- # KSA Workspace Standards > Baseline rules for all KSA Technologies Claude workspaces. > Source of truth: `/mnt/c/Users/Kevin/OneDrive - ksatechnologies.com/workspace/claude-scaffold/CLAUDE.md` ## Environment - **Shell:** WSL (Linux). Always use bash/Linux syntax — `export VAR=value`, forward slashes, `source .venv/activate`. Never PowerShell, never cmd.exe. - **OS:** WSL2 on Windows. Paths under `/mnt/c/Users/Kevin/OneDrive - ksatechnologies.com/workspace/`. ## Agent / Minion Rules - **Minions are research-only.** They search, read, and web-fetch. They return findings to main Claude. They never write files, commit, push, or delete. - **Minions do not talk to each other.** All findings route through main Claude. - **Main Claude does all writing, committing, and pushing.** ## File Rules - **New files:** write freely. - **Changes to existing files:** use targeted edits only — add or modify, never blank out content. - **Total replacement of an existing file:** write the replacement as a NEW file first, explain to the user why the old file needs replacing, wait for explicit approval. Never silently overwrite. - **Briefs and reports:** always new dated files. Never overwrite a previous brief. The historical record grows forward, never shrinks. ## Permission Levels | Actor | Permissions | |---|---| | **User** | Full control — can do anything including delete | | **Main Claude** | Pre-approved: read, write, edit, commit, push, gh CLI. Must ask before any delete unless user has explicitly pre-authorized it. | | **Minions** | Research only — read, search, web-fetch. No write, commit, push, or delete. | ## Workflow Order Always follow this sequence. Never skip ahead: 1. **Open a GitHub issue** — captures the problem/feature before any code 2. **Update design/architecture docs** — ADR if warranted, roadmap, architecture 3. **Implement code** — with tests 4. **Update docs** — test-scenarios, user examples, troubleshooting callouts 5. **Close the issue** — with version, commit, and doc pointers ## Documentation Discipline Every feature, breaking change, or config change ships with: - Code + tests - Test-scenario examples - Troubleshooting callouts where relevant - Architecture/design doc update if the shape changed - ADR if a significant technology decision was made - Roadmap `Resolved` update Never ship a feature without its docs. ## Issue Lifecycle - Close issues when work ships — include version, commit SHA, and doc pointers - User-filed bugs: add `fixed — awaiting-feedback` label if user confirmation needed - Dependabot patch/minor: merge when CI is green (ad-hoc, no milestone) - Dependabot major: open an issue, add to a milestone, test intentionally - Dependabot CVE: link PR to the security issue, include in relevant milestone ## SAFe Hierarchy (GitHub Issues) | Level | GitHub object | When to use | |---|---|---| | **Milestone** | GitHub Milestone | PI / sprint container, ties to a semver release | | **Feature** | Issue (`enhancement` label + milestone) | Single deployable unit of value — this is the default issue type | | **Epic** | Issue (`epic` label, no milestone, task list of linked features) | Only when work spans multiple milestones | | **Story** | Sub-task within a feature | Only when multiple contributors need independently assignable units | Default: **milestone → feature**. Add epic and story layers only when the team size and cross-milestone scope justify the overhead. ## Technology Governance Follow the original developer when a project forks due to governance or ownership breakdown. Validated examples: - pfSense → **OPNsense** (m0n0wall founder Kasper endorsed it) - CentOS → **Rocky Linux** (co-founder Kurtzer created it within days) - nginx → watch **freenginx** (core developer Dounin forked over F5 governance) - OpenOffice → **LibreOffice** (original developers moved there) **Avoid:** Oracle (hostile OSS track record), F5/nginx (governance broken), Bitnami (stale images since 2024 paid tier split). **Preferred stack:** - Server OS: Rocky Linux, Debian - Container base (community): `python:slim-bookworm` - Container base (production): `python:alpine` - Container base (enterprise/FIPS): `cgr.dev/chainguard/python` - CNI: Cilium - Ingress: Kubernetes Gateway API (Cilium native implementation) - Secrets: HashiCorp Vault sidecar / CSI driver - Postgres (single node): `postgres:16-alpine` custom subchart - Postgres (HA): CloudNativePG operator ## ADR Process ADRs follow an RFC lifecycle: **Draft → Proposed → Accepted / Rejected / Withdrawn**. When a decision changes, a new ADR is created and the old one is marked **Superseded** — the historical record is never deleted. See `ADR-000-process.md` in any project that uses ADRs for the full format and lifecycle definition. ## Git Commit Style - Conventional commits: `feat:`, `fix:`, `docs:`, `chore:`, `ci:`, `refactor:` - Co-author line on every commit: `Co-Authored-By: Claude Sonnet 4.6 ` - Commit message: why, not what. The diff shows what; the message explains why. - Never amend published commits. Create new commits instead. - Never skip hooks (`--no-verify`) unless explicitly instructed. ## Memory System Memory lives at: `.claude/projects//memory/MEMORY.md` On starting a new session, read `MEMORY.md` first. It indexes all project-specific memories. Update memory when: - User corrects an approach or confirms a non-obvious one - A significant project decision is made - User preferences or constraints are stated Never save ephemeral task details to memory — use the todo list for that.