# Phase 2 Verification — Orca v0.6 P02

**Phase**: P02 — Proxmox SSH Join
**REQ Coverage**: REQ-050, REQ-051
**Verification date**: 2026-08-03
**Result**: ✅ PASS (all 4 layers; integration test against real PVE deferred — unit tests cover all logic)

## Structural Verification

- ✅ `go build ./...` — PASS
- ✅ `go vet ./...` — PASS
- ✅ `gofmt -l .` — PASS (all Go files formatted)
- ✅ `make lint` — PASS
- ✅ `golang.org/x/crypto v0.54.0` added as direct dep (D-030); transitive: x/sys v0.47.0, x/term v0.45.0
- ✅ `internal/proxmox` new package follows existing package layout conventions
- ✅ `internal/security/sshkey.go` follows the CAInit pattern (idempotent fast-path, writeAtomic, mode enforcement)

## Behavioral Verification

### REQ-050: Proxmox SSH bootstrap via golang.org/x/crypto/ssh
- ✅ `TestGenerateOrLoadSSHKey_Generates`: Ed25519 keygen, 0600/0644 modes, ssh-ed25519 pub format, ssh.ParsePrivateKey round-trip
- ✅ `TestGenerateOrLoadSSHKey_IdempotentLoad`: second call loads existing (D-036)
- ✅ `TestGenerateOrLoadSSHKey_CreatesDir`: nested dir creation
- ✅ `TestBootstrapProxmox_Validation`: missing host → error, missing password → error
- ✅ `TestDefaultOptions`: DefaultProxmoxUser=orca, DefaultProxmoxRole=OrcaOperator, DefaultSSHPort=22
- ✅ CLI `--type proxmox --host ... --password ...` flag wiring verified via `orca node join --help`
- ✅ Password from `--password` flag OR `$ORCA_PROXMOX_PASSWORD` env var (D-031)
- ✅ TOFU host-key via `knownhosts.New` (D-035, avoids deprecated InsecureIgnoreHostKey)
- ✅ File upload via session heredoc (no SFTP dep — D-030)

### REQ-051: OrcaOperator role + orca@pam user + sudoers
- ✅ `TestSudoersContent`: NOEXEC on pct/qm, NOPASSWD on apt-get/dpkg (no NOEXEC), pvesh excluded from command lines (AD-020)
- ✅ `TestSudoersContent_CustomUser`: custom user name works
- ✅ `TestOrcaOperatorPrivileges`: exactly 3 privileges (VM.Audit, Datastore.AllocateSpace, SDN.Use) space-separated (D-033)
- ✅ `orca@pam` realm (AD-019 — not @pve)
- ✅ `pveum` commands use `--privs` (space-separated), probe-then-add idempotency pattern
- ✅ `visudo -cf` validation step aborts bootstrap on syntax error
- ✅ Node registered with kind=proxmox, os=pve

## Security Verification

- ✅ SSH private key mode 0600 enforced (TestGenerateOrLoadSSHKey_Generates)
- ✅ SSH public key mode 0644 enforced
- ✅ Password never persisted (D-031) — used only for SSH auth, zeroed after use
- ✅ Password from env var preferred over flag (reduces ps/proc exposure)
- ✅ pvesh excluded from sudoers (AD-020 — API execute bypasses NOEXEC)
- ✅ NOEXEC on pct/qm (blocks shell escapes via dynamically-linked perl)
- ✅ TOFU host-key pinning (D-035) — capture on first connect, verify on subsequent, fail closed on mismatch
- ✅ No secrets in logs (audit log entries contain host, user, role — never password)
- ✅ sudoers file mode 0440 enforced (sudo requirement)

## Quality Verification

- ✅ `go test -race -count=1 ./internal/proxmox/... ./internal/security/... ./internal/cli/...` — all PASS
- ✅ Test coverage: sshkey (4 tests), proxmox (5 tests), sudoers content (2 tests), privileges (1 test), validation (1 test), defaults (1 test)
- ✅ Error wrapping with `fmt.Errorf("...: %w", err)` (REQ-018)
- ✅ `context.Context` propagation (REQ-017)
- ✅ Idempotency: all bootstrap steps probe-before-add (D-036)
- ✅ New direct dep: 1 (golang.org/x/crypto) — matches D-030 minimal-deps rationale

## Integration Test Note

A live integration test against a real Proxmox VE 8/9 host is out of
scope for automated CI (requires a PVE host + credentials). The SSH
bootstrap logic is tested via:
- Unit tests for command builders (sudoers content, privilege set)
- Unit tests for validation (missing host/password)
- Unit tests for SSH key generation (Ed25519, modes, idempotency)
- Manual verification via `orca node join --help` (flag surface)

A `// +build integration` test against a real PVE host can be added
in a future phase if a PVE test environment becomes available.

## Must-Have Checklist

- [x] `go.mod` / `go.sum` — golang.org/x/crypto v0.54.0
- [x] `internal/certpaths/certpaths.go` — SSHKeyPath, SSHPubPath, KnownHostsPath
- [x] `internal/security/sshkey.go` — GenerateOrLoadSSHKey (Ed25519)
- [x] `internal/proxmox/bootstrap.go` — BootstrapProxmox full SSH dance
- [x] `internal/cli/node.go` — --type/--host/--password flag wiring + joinProxmox
- [x] `internal/security/sshkey_test.go` — 4 tests
- [x] `internal/proxmox/bootstrap_test.go` — 5 tests

## Escalations

None.