9.7 KiB
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:
- Deploying a new Perl LunCmd handler (
FreeNAS.pm) to/usr/share/perl5/PVE/Storage/LunCmd/ - 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)
- A push to this repo triggers
action.ymlwhich fires arepository_dispatchevent toTheGrandWazoo/freenas-proxmox-packer - That separate repo holds the DEBIAN package structure (
DEBIAN/control,postinst,postrm,triggers) - Its CI builds the
.debwithdpkg-deband 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
postinstclones the repo at install time (requires internet, fragile)Custom/FreeNAS.pmis unfinished (duplicateproperties()andoptions()subs)- REST::Client is both an apt dependency AND manually bundled
- Everything is named
FreeNASinternally but the product is nowTrueNAS - 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-proxmoxstable,ksatechnologies/truenas-proxmox-testingbeta) - Install:
apt install freenas-proxmoxafter adding the repo - The package uses dpkg
triggersto 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:
- Open a GitHub issue — captures the problem/feature before any code
- Update design/architecture docs — ADR if warranted, roadmap, architecture
- Implement code — with tests
- Update docs — test-scenarios, user examples, troubleshooting callouts
- 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
Resolvedupdate
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-feedbacklabel 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-alpinecustom 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 <noreply@anthropic.com> - 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/<workspace-path>/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.