Files
orca/.ciagent/REQUIREMENTS.md
T
Jon Chery 8071793260 docs(init): validate specification — v0.13 Production Hardening Round 2 + UAT Plan
15 new requirements (REQ-149..REQ-163), 14 phases (P0+P01..P12+P13).
Three deep codebase sweeps surfaced ~60 gaps beyond v0.12:
- orca job run runs locally (scheduler/emitter/SSH-push dead code)
- jobspec schedule/timeout silently dropped (DaemonSet broken)
- acl.Check called zero times (v0.12 zero-trust not wired)
- command injection vectors (logs --job, tar-slip, sudoers, txn rollback)
- Go toolchain 1.25.0 (24 stdlib vulns)
- concurrency hazards (audit chain race, secrets data loss, no busy_timeout)
- cache never invalidated by writes
- massive doc drift (README mTLS claim false, cli.md missing 25 subcommands)

v1.0.0 stays deferred for post-v0.13 UAT signoff.

---ci---
project: orca
phase: 0
milestone: v0.13
status: specify
---/ci---
2026-08-07 18:43:04 +00:00

60 KiB
Raw Blame History

Requirements: Orca

The canonical requirements table. Each row carries the REQ-ID, the milestone it belongs to, the requirement summary, priority, the phase that addresses it, and the current status. This single table is the source of truth — superseded any per-milestone status tables in earlier versions of this file.

ID Requirement Priority Phase Status
REQ-001 Go 1.25+ toolchain support High v0.1 P01 Complete
REQ-002 CLI-first interface for all operations (single binary) High v0.1 P01 Complete
REQ-003 Offline-first operational mode (no cloud deps) High v0.1 Complete
REQ-004 Basic task deployment (single-node process execution) Medium v0.1 P03 Complete (single-node); multi-node dispatch in v0.2 P02
REQ-005 Local state storage via modernc/sqlite (CGO-free) Medium v0.1 P02 Complete
REQ-006 Security-first audit logging via log/slog High v0.1 P04 Complete
REQ-007 CoreCI full release flow integration via .coreci.yml High v0.1 P06 Complete (per-phase releases)
REQ-008 Structured JSON logging (slog) High v0.1 P05 Complete
REQ-009 HCL/YAML job spec parsing Medium v0.1 P03 Complete
REQ-010 --json output flag for machine consumption High v0.1 P01 Complete
REQ-011 mTLS for inter-node communication Medium v0.2 P01 Complete (P01 shipped v0.2.1)
REQ-012 ~/.orca/config.hcl and /etc/orca/orca.hcl config locations Low v0.1 P01 Complete (CLI uses ~/.orca/ + ORCA_DB env)
REQ-013 Pre-push git hook triggers CoreCI on every push High v0.1 P01 Complete
REQ-014 gosec + govulncheck in CI pipeline High v0.2 P03 Complete (P10 shipped v0.2.3)
REQ-015 MIT LICENSE Low v0.1 P01 Complete
REQ-016 README.md with quickstart Medium v0.1 P01 Complete
REQ-017 context.Context propagation in all I/O High v0.1 Complete
REQ-018 Error wrapping with fmt.Errorf("...: %w", err) High v0.1 Complete
REQ-019 Cobra CLI framework High v0.1 P01 Complete
REQ-020 HCL parser integration (hashicorp/hcl) Medium v0.1 P03 Complete
REQ-021 os/exec with WaitDelay (Go 1.25+) Medium v0.1 P03 Complete
REQ-022 iter.Seq for streaming job lists (Go 1.25+) Low v0.3 P01 Complete (v0.3 P01 shipped v0.3.1)
REQ-023 Self-signed mTLS cert generation Medium v0.2 P01 Complete (P01 shipped v0.2.1)
REQ-024 Makefile with standard targets High v0.1 P01 Complete
REQ-025 Bounded cert rotation history: retain last N=3 server certs per node for rollback Medium v0.2 P01 Complete (P01 shipped v0.2.1)
REQ-026 Trusted-CA fingerprint pinned in config; daemon refuses to start on mismatch High v0.2 P01 Complete (P01 shipped v0.2.1)
REQ-027 govulncheck runs in offline mode in CI (no vuln.go.dev calls; pre-mirrored DB or -format json + jq gate) High v0.2 P03 Complete (P10 shipped v0.2.3)
REQ-028 HCL/YAML schema for NodeCapacity declaration (orca node join flag and/or ~/.orca/node.hcl) High v0.2 P02 Complete (P09 shipped v0.2.2; orca node capacity CLI)
REQ-029 gitleaks baseline file committed to repo to suppress pre-existing .env SHA-1 leak in git history Medium v0.2 P03 Complete (P10 shipped v0.2.3)
REQ-030 --watch output format mode: table (default) vs streaming one-line JSON per event Low v0.3 P01 Complete (v0.3 P01 shipped v0.3.1)
REQ-031 go test -race enabled in CI for all v0.2 packages High v0.2 P01P04 Complete (P10; .coreci.yml test pipeline runs -race)
REQ-032 orca doctor subcommand for diagnostics (CA/cert health, db integrity, peer reachability) Medium v0.2 P01 / v0.3 P02 Complete (cert checks P01 v0.2.1; network + db P02 v0.3.2)
REQ-033 Cert file mode enforcement: 0600 for keys, 0644 for certs (refuses to start on violation) High v0.2 P01 Complete (P01 shipped v0.2.1)
REQ-034 Cert proactive rotation alarm: structured slog WARN 30 days before not_after Medium v0.2 P01 Complete (P01 shipped v0.2.1)
REQ-035 orca cert show redacts private key material from default and --json output High v0.2 P01 Complete (P01 shipped v0.2.1)
REQ-036 Server cert SAN validation: SAN entries (DNS + IP) populated at sign-time; refuses to sign a CSR without them High v0.2 P01 Complete (P01 shipped v0.2.1)
REQ-037 X-Orca-Idempotency-Key header on cross-node POST; dispatcher retries only when header is present Medium v0.2 P02 Complete (P09 shipped v0.2.2; internal/transport/idempotency.go)
REQ-038 Structured slog fields for mTLS failures: event=mtls.handshake, peer, cert_fp, err Medium v0.2 P01 Complete (P01 shipped v0.2.1)
REQ-039 .gitleaks.toml extended with stopwords for test data paths and CA cert PEM blocks Medium v0.2 P03 Complete (P10 shipped v0.2.3)
REQ-040 .golangci.yml unified lint config superseding per-tool invocations Low v0.2 P03 Complete (P10 shipped v0.2.3)
REQ-041 Unified namespace root via ORCA_HOME for all components (db, certs, init, daemon) High v0.5 P1 Complete (P1 shipped v0.4.2)
REQ-042 --system flag selects system-level namespace root /root/.orca High v0.5 P1 Complete (P1 shipped v0.4.2)
REQ-043 install.sh 1-liner pulling release binary from public Gitea URL; user-level default, --system for system-level High v0.5 P2 Complete (P2 shipped v0.4.3)
REQ-044 install.sh in-place update preserves config/state; idempotent re-run High v0.5 P2 Complete (P2 shipped v0.4.3)
REQ-045 Gitea repo + releases publicly accessible (unauthenticated download) High v0.5 P0 Complete (P0 ship: repo + org visibility public)
REQ-046 Docker image published to Gitea container registry per release Medium v0.5 P3 Complete (P3 shipped v0.4.4)

v0.1 Milestone Summary

Status: Complete — all 6 phases shipped (P00P06) plus P07 backfill, 4-layer verification passed at every phase, tagged v0.2.0 per run.md versioning logic (next-minor after all feature-patches v0.1.1..v0.1.7 ship).

Coverage: 21/24 v0.1-declared requirements complete by v0.1 ship; the 3 deferred (REQ-011, REQ-014, REQ-022, REQ-023) all moved to v0.2. Plus REQ-025..REQ-040 (16 net-new) added by v0.2 IDEATE stage.

v0.2 Milestone Summary

Status: Functionally Complete (pending merge to main) — P08 (mTLS), P09 (scheduling), P10 (security scan) all shipped to the milestone/v0.2-networking-observability-security branch as v0.2.1, v0.2.2, v0.2.3. The milestone branch has NOT been merged to main yet. REQ-022/030 (iter.Seq streaming) and REQ-032 (doctor network/db) were deferred to v0.3.

v0.3 Milestone Summary

Status: Complete — P01 (iter.Seq streaming, v0.3.1) and P02 (doctor network+db, v0.3.2) both shipped. REQ-022, REQ-030, REQ-032 all complete. Re-init SPECIFY audit confirmed all other v0.2-deferred REQs (014, 027, 028, 029, 031, 037, 039, 040) already shipped in P08-P10.

Deferred to v0.4

  • pprof endpoint on orca daemon (idea I-308, 0.70 confidence): deferred to keep v0.2 lean; revisit in v0.3 once P02's dispatcher is stable.

v0.5 Milestone Summary

Status: Complete — all 3 execution phases + final review shipped. P0 (v0.4.1), P1 (v0.4.2), P2 (v0.4.3), P3 (v0.4.4), P4 final (v0.4.5). REQ-041..046 all complete. Repo + releases publicly accessible (REQ-045). Docker image published to Gitea container registry (REQ-046).

  • P0 (v0.4.1): pre-execution + repo visibility flipped to public (REQ-045).
  • P1 (v0.4.2): namespace unification — ORCA_HOME + --system (REQ-041/042).
  • P2 (v0.4.3): install.sh 1-liner + in-place update (REQ-043/044) + README quickstart (REQ-016).
  • P3 (v0.4.4): Docker release — distroless image + Gitea container registry (REQ-046).
  • P4 (v0.4.5): final review + audit + milestone release.

v0.6 Requirements — Node Bootstrap & Proxmox

ID Requirement Priority Phase Status
REQ-047 orca init auto-provisions CA + server cert + DB migrations + localhost node (idempotent; safe re-run) High v0.6 P1 Complete (P1 shipped v0.5.1)
REQ-048 orca init registers a default localhost node with auto-detected OS via /etc/os-release ID High v0.6 P1 Complete (P1 shipped v0.5.1)
REQ-049 Node schema extension: nodes.kind (localhost|linux|proxmox) + nodes.os columns (migration 0006, backward-compatible) High v0.6 P1 Complete (P1 shipped v0.5.1)
REQ-050 orca node join --type proxmox SSH bootstrap via golang.org/x/crypto/ssh (new direct dep); password auth, deploy orca pubkey, create orca user (config-overridable), assign PVE role, drop sudoers allowlist; idempotent High v0.6 P2 Complete (P2 shipped v0.5.2)
REQ-051 Proxmox least-privilege OrcaOperator PVE role (VM.Audit, Datastore.AllocateSpace, SDN.Use) + orca user + /etc/sudoers.d/orca allowlist (pct, qm, pvesh, apt-get, dpkg); config-overridable user/role names High v0.6 P2 Complete (P2 shipped v0.5.2; refined: pvesh excluded per AD-020, orca@pam per AD-019)
REQ-052 orca doctor extensions: doctor os (verify localhost OS detection matches stored node row) + doctor proxmox (SSH-probe each kind=proxmox node with pveversion/pvecmd status, 3s timeout, PASS/WARN/FAIL); audit log all bootstrap + join actions Medium v0.6 P3 Complete (P3 shipped v0.5.3)

v0.6 Milestone Summary

Status: Complete — all 3 execution phases + final review shipped. P0 (v0.5.0), P1 (v0.5.1), P2 (v0.5.2), P3 (v0.5.3), P4 final (v0.5.4). REQ-047..052 all complete.

  • P0 (v0.5.0): pre-execution (specify → clarify → research → plan). 8 decisions (D-030..D-037).
  • P1 (v0.5.1): orca init full bootstrap + schema 0006 (REQ-047/048/049).
  • P2 (v0.5.2): Proxmox SSH join + OrcaOperator role + sudoers (REQ-050/051).
  • P3 (v0.5.3): doctor os + doctor proxmox + audit logging (REQ-052).
  • P4 (v0.5.4): final review + audit + milestone release.

v0.7 Requirements — Hardening & Completion

ID Requirement Priority Phase Status
REQ-053 orca cert command tree registered on root command (cert ca-init, cert gen, cert show, cert renew, cert fingerprint) — code exists in internal/cli/cert.go but is never AddCommand'd; unreachable today High v0.7 P1 Complete (P1 shipped v0.6.1)
REQ-054 HCL config file parsing: internal/config package loads ~/.orca/config.hcl / /etc/orca/orca.hcl (keys: db_path, listen_addr, ca_path, server_cert_path, server_key_path, node_capacity); merge precedence flag > env > file > default; --config flag on root command High v0.7 P2 Complete (P2 shipped v0.6.2)
REQ-055 Test coverage uplift: every package ≥ 50% — adds tests for internal/engine (executor, dispatcher, peer), internal/transport (mtls, dispatch, handshake_log), internal/proxmox (bootstrap SSH path), internal/audit Medium v0.7 P3 Complete (P3 shipped v0.6.3)
REQ-056 --pprof <addr> opt-in flag on orca daemon (default disabled); net/http/pprof mounted on a separate mux, never on the mTLS daemon listener Low v0.7 P4 Complete (P4 shipped v0.6.4)

v0.8 Requirements — Coverage & Trust Hardening

ID Requirement Priority Phase Status
REQ-057 Test coverage uplift round 2: raise internal/engine (8.3%), internal/proxmox (5.1%), internal/cli (27.6%), internal/transport (26.3%), internal/store (46.7%), internal/jobspec (47.6%) to ≥ 70%; add first tests for internal/audit, internal/certpaths, cmd/orca (currently 0%) to ≥ 50% (D-047 tiered floor) High v0.8 P1 Complete (P1 shipped v0.7.1; all 9 packages exceeded floor)
REQ-058 --host-key-fingerprint <SHA256:base64> pre-pin flag on orca node join (validated when --type proxmox): when supplied, join fails fast if the SSH host key's OpenSSH SHA-256 fingerprint does not match; supersedes TOFU (D-035) for pre-pinned deployments (D-044, D-045) Medium v0.8 P2 Complete (P2 shipped v0.7.2)
REQ-059 orca node key-reset <node> command: clears the persisted SSH host key entry for the node from ~/.orca/known_hosts only (local, not remote authorized_keys — D-046); audit-logs event=node.key_reset; next doctor proxmox/dispatch re-pins via TOFU or --host-key-fingerprint Low v0.8 P2 Complete (P2 shipped v0.7.2)
REQ-060 Requirement-status hygiene sweep: REQUIREMENTS.md v0.7 rows were stale ("Pending" after ship); add a verify-stage assertion that every REQ listed as Complete in ROADMAP.md has a matching Complete row in REQUIREMENTS.md, enforced by make verify-reqs Medium v0.8 P3 Complete (P3 shipped v0.7.3)

v0.9/v0.10 Requirements — Re-architecture Foundation & Production Hardening

The v0.9/v0.10 milestones supersede the shipped v0.1v0.8 architecture per the adopted PRD (.ciagent/PRD_v0.9.md). The re-architecture is justified on six grounds recorded in the PROJECT.md Supersession Table. 30 net-new requirements (REQ-061..REQ-090) derive from the v0.9 IDEATION; their phase placement and binding grill conditions (C-01..C-19) are documented in IDEATION_v0.9.md and GRILL_v0.9.md.

ID Requirement Priority Phase Status
REQ-061 orca daemon deprecation command and build-tag removal path: v0.9 emits deprecation warning + still runs (dual-write window); v1.0 repurposes to orca daemon drain-and-stop (stops v0.8 daemons on peers via SSH, confirms workloads survive via systemd); post-v1.0 the command and internal/daemon/ are deleted. // Deprecated Go doc comments + slog.Warn on every run (I-M-001) High v0.11 P14b (drain-and-stop + rotate-lead) Complete
REQ-062 Coverage follow-ups: 3 zero-test packages (internal/audit, internal/certpaths, cmd/orca) + internal/cli to 70% floor; once daemon.go is deprecated/removed the exclusion reason disappears and the floor applies to the whole package; all net-new subsystems carry a 70% floor from their first phase (I-M-002) Medium v0.9 P0X + each new pkg Complete
REQ-063 known_hosts flock concurrency gap (deferred P1 from REVIEW_v0.8 A2): add flock-style advisory lock (stdlib syscall.Flock wrapper) around the read-modify-write in TOFUHostKeyCallback capture path (bootstrap.go:290-302) and ResetHostKey (bootstrap.go:479-523); lock file at cluster/known_hosts.lock (R-002) (I-M-003) Medium v0.9 P0a1 Complete
REQ-064 HCL→Markdown jobspec adapter/bridge layer: keep internal/jobspec/spec.go as legacy HCL path behind // Deprecated; add internal/jobspec/markdown.go (canonical) + internal/jobspec/dispatch.go (extension-based dispatcher: .md→Markdown, .hcl→legacy, .yaml→Markdown-with-empty-body); unified *WorkloadSpec populated via adapter; preserves orca job run old-spec.hcl during migration window (I-M-004) High v0.9 P0b Complete
REQ-065 orca doctor --legacy-paths detection: detects v0.8 residue (orca.db at ORCA_HOME root, ca.crt/ca.key, config.hcl, flat server.crt, namespace column in any *.db); outputs list of legacy artifacts with migration recommendations; the detection half of v0.10-P14 (I-M-005) Medium v0.11 P14c Complete
REQ-066 Legacy CA state migration to step-ca: orca upgrade --to-v1.0 --import-ca reads ~/.orca/ca.key, initializes step-ca with it, re-issues workload SVIDs; preserves audit history even if live trust root changes (I-M-006). Gated by C-07 High v0.11 P14a Complete
REQ-067 Fuzz test harness for Markdown frontmatter parser: testing.F fuzz target in internal/jobspec/markdown_test.go round-trips random frontmatter+body through ParseMarkdown asserting byte-exact body preservation; corpus of adversarial fixtures (CRLF, BOM, no-frontmatter, empty-frontmatter, frontmatter-with-only-separator) (I-M-007) Medium v0.9 P0b Complete
REQ-068 Deprecation warnings on removed/repurposed CLI subcommands: each removed/changed command (orca cert, orca node join mTLS semantics, orca job run <spec.hcl>) emits slog.Warn deprecation banner with v1.0 replacement except under orca upgrade; --no-deprecation-warnings global flag via root.go PersistentPreRunE (I-M-008) Low v0.9 P0X + v0.10 P13 Complete
REQ-069 internal/config/config.go HCL config demotion via adapter: keep internal/config/ as legacy_config.go with // Deprecated; add internal/config/markdown.go for new Markdown-frontmatter loader (R-014); root.go dispatches on file extension (.hcl→legacy, .md→new); --config semantics: .hcl read-only legacy, .md canonical (I-M-009) High v0.9 P0a1 Complete
REQ-070 internal/certpaths/ replacement with multi-namespace path resolver: new internal/paths package with paths.NamespaceDir(ns), paths.ClusterDir(), paths.CacheDB(), paths.MasterKey(), paths.NSDb(ns), paths.NSEnv(ns), paths.NSSecrets(ns); keep certpaths as thin shim for v0.8 compat then remove post-v1.0 (R-002) (I-M-010) — highest blast radius High v0.9 P0a1 Complete
REQ-071 internal/store/ schema: per-namespace DBs, drop namespace column: store.Open gains namespace parameter (or caller passes paths.NSDb(ns)); migrate.go runs migrations per namespace DB; cert_repo (0004) removed (step-ca handles certs); audit_log moves to CLI-side cache DB (R-008) (I-M-011) High v0.9 P0a1 + v0.10 P06 Complete
REQ-072 internal/transport/ deletion + SSH-push package: delete mtls.go, dispatch.go, handshake_log.go; extract retry/idempotency patterns into internal/sshpush/; existing transport.IdempotencyStore directly reusable (I-M-012). Deletion deferred to v0.10-P14 to keep dual-write window open High v0.9 P00 (delete v0.10 P14) Complete
REQ-073 SSH-push transport layer design: connection pooling (reuse *ssh.Client per peer), idempotency (content-addressed filenames), retry (exponential backoff 100ms×2 cap 5s max 5), timeout (30s SCP, 10s exec), fan-out (errgroup bounded concurrency default 8), known_hosts reuse proxmox.TOFUHostKeyCallback (I-B-001) High v0.9 P01 (design P0a1) Complete
REQ-074 Emitter template system (Layer 4): internal/emitter/ package with Emitter interface Render(spec *WorkloadSpec, node *Node) ([]File, error); implementations systemdEmitter/traefikEmitter/syncthingEmitter/socketEmitter; SSH-push SCPs []File atomically (write-to-tmp + rename); emitters registered per kind + runtime (I-B-002) High v0.9 P0c Complete
REQ-075 Lead applier execution model: CLI renders transaction bundle (tarball + apply.sh + verify.sh) on operator host, SCPs to lead's /run/orca/txns/<txn-id>/, lead's systemd timer runs apply.sh idempotently, CLI polls txn status via SSH; bash scripts generated by emitter not hand-written (I-B-003). Gated by C-09 High v0.11 P10a Complete
REQ-076 step-ca integration: orca init runs step ca init on lead; CLI SSHs to lead, installs step-ca via apt, stores step-ca.json; workload SVIDs via step ca token (JWE minted by CLI) → step ca certificate; SPIFFE ID as SAN; new internal/stepca/ package wraps step CLI via SSH (I-B-004). Reverses AD-010 per override justification ground 2 High v0.9 P07 + v0.10 P02 Complete
REQ-077 Traefik dynamic config generation + atomic reload: Traefik emitter renders /etc/traefik/dynamic/orca-<ns>-<svc>.yaml with backends (socket paths R-007), health checks, mTLS config pointing at step-ca root; atomic reload via tmpfile+fsync+rename triggering fsnotify; drain writes weight=0 or removes backend (I-B-005). Gated by C-10 High v0.9 P02 Complete
REQ-078 Runtime abstraction interface (5 backends): Runtime interface in internal/runtime/ with Prepare/Start/Stop/Status; processRuntime (wraps existing executor.go), wasmRuntime (wasmtime via SSH), podmanRuntime, pveVMRuntime (qm via proxmox SSH), pveCTRuntime (pct); runtimeRegistry keyed by runtime: frontmatter value; Alloc carries runtime field changeable on migration (I-B-006). Split P07a/b/c per PC-10. P07b gated by C-01 High v0.9 P07a/b/c Complete
REQ-079 Transaction bundle format + N-peer atomicity: bundle = tarball with desired-state.json + apply.sh + verify.sh + rollback.sh + manifest.sig (signed with master.key); content-addressed <txn-id>=sha256(desired-state.json) stored in cluster/txns/<txn-id>/; lead applies to self first then fans out; failure on any peer runs rollback.sh on applied peers (I-B-007). Gated by C-09 High v0.11 P10a Complete
REQ-080 Master key management + HKDF-SHA256 per-line .env.secrets encryption: cluster/master.key 32-byte random (generated at orca init using WriteAtomic pattern); each line `base64(nonce ciphertext
REQ-081 Syncthing config rendering + folder-ID content-addressing: per-namespace Syncthing folder orca-<ns> with content-addressed folder ID sha256(ns + master-key-fingerprint); CLI renders config.xml per peer; Syncthing runs as systemd unit (emitted by systemd emitter); CLI discovers peers via cluster/peers/; migration works because new node joins folder and syncs before workload starts (I-B-009). Gated by C-02 + C-14 Medium v0.9 P09 (spike v0.9 P00) Complete
REQ-082 Namespace inheritance resolver algorithm: DFS parent walker with visited set for cycle detection; _defaults/ implicit root (always exists, no parent); merge semantics: child overrides parent for scalars, arrays unioned (child adds to parent); pure function (no I/O) taking map[nsName→*NSConfig] returning map[nsName→*ResolvedNS] (I-B-010) High v0.9 P0a2 Complete
REQ-083 CLI-side scheduler redesign: Score(node, workload) (score int, fits bool) where fits checks runtime compatibility + constraints, score is bin-packing (most free capacity = highest); Services pick count distinct nodes (anti-affinity default); DaemonSets pick all matching nodes; Job = one-shot; CLI-side not daemon-side (R-001) (I-B-011) High v0.9 P05 (skeleton P0c) Complete
REQ-084 orca job lint category-driven lint engine: Linter runs Rule checks returning Finding{Category, Severity, Message, Explanation}; categories schema/runtime/security/migration/best-practice; --explain prints rationale; pure (no I/O) checks against static rules (I-B-012) Medium v0.11 P11 Complete
REQ-085 v0.8→v1.0 migration ordering: v0.9 ships new parser + kinds + runtime + SSH-push alongside old daemon (dual-write window); orca job run dispatches on extension (.md→SSH-push, .hcl→old daemon); v0.10-P05 drains old daemons; v0.10-P14 converts remaining .hcl specs and removes daemon (I-C-001). Most important cross-cutting idea High v0.9 P00 → v0.10 P14 Complete
REQ-086 "No orca on server" enforcement: orca doctor no-orca-on-server SSHs to each peer verifying no orca binary in PATH, no orca systemd service, no orca process, no /etc/orca/ directory; runs after v0.10-P05 before v0.10-P16; reuses v0.8 proxmox SSH session infrastructure (I-C-002). Implements grill C-13 High v0.11 P14c Complete
REQ-087 Test infrastructure: hermetic 3-linux + 1-proxmox cluster pipeline: test/integration/ with docker-compose/vagrant creating 4 containers/VMs; Go test harness SSHes to each, runs CLI, asserts end-to-end workflows (ns create → workload submit → migrate → drain); proxmox simulated via mock pct/qm; v0.8 e2e tests (bootstrapE2ESetup) are foundation (I-C-003) Medium v0.11 P08 Complete
REQ-088 Security-engineer + network-engineer persona reactivation: reactivate security-engineer (step-ca provisioner model, SSH-push blast radius, Traefik edge, .env.secrets crypto) and network-engineer (socket exposure R-007, Syncthing P2P ports, Traefik routing); cross-cutting review not single phase (I-C-004). Implements grill C-05 High v0.9 P00 → v0.10 P16 Complete
REQ-089 Documentation rewrite: ARCHITECTURE.md/PROJECT.md/README + AD-010 supersession: v0.9-P00 adds "v0.9 Architecture (Supersedes v0.8)" section + banners + Superseded Decisions table; v0.10-P15 rewrites README quickstart for new curl sh + orca init + orca ns create flow (I-C-005) Medium v0.9 P00 + v0.10 P15/P16
REQ-090 Dual-write window: v0.9 orca job run dispatches on extension (.md→SSH-push new path, .hcl→old daemon path) via parser dispatcher (REQ-064); daemon not removed until v0.10-P05; SSH-push path writes to separate systemd unit namespace (orca-v1-<alloc>.service) while daemon uses orca-<job>.service — no unit name overlap = no conflict (I-C-006) High v0.9 P00 Complete

v0.10 Docs & Install Milestone Requirements

The following requirements are scoped to the v0.10 docs/cli-examples milestone. They cover the CLI reference documentation, jobspec reference, ingress guide, full-stack example jobspecs, README refresh, namespace.md v0.9 layout update, and the release/install pipeline fix that guarantees every Gitea release carries a Linux binary asset.

ID Requirement Priority Phase Status
REQ-091 docs/cli.md comprehensive CLI reference: every command/subcommand with synopsis, flags (name/type/default/description), and one-line example; global flags (--json, --system, --config, --no-deprecation-warnings); output modes (text vs --json, --watch table vs NDJSON); exit codes; deprecated surface (orca daemon, orca cert, orca node join mTLS path, legacy .hcl jobspec) flagged with callout boxes pointing to v0.10 removal High v0.10 P2 Complete
REQ-092 docs/jobspec.md markdown frontmatter schema reference: all top-level keys, block reference (runtime, ports, env/secrets, volumes, restart, update, service, health, lifecycle, constraints, affinity, tasks), kinds matrix (Job/Service/DaemonSet required vs allowed), CEL subset grammar, body byte-exact preservation (R-015), deprecated HCL form callout High v0.10 P2 Complete
REQ-093 docs/ingress.md Traefik ingress reference: kind: Service implies Traefik route (D-175), R-007 socket-vs-TCP-bind semantics, generated Traefik YAML shape (routers/services/healthCheck), atomic reload (C-10), drain (weight: 0), TLS (certResolver, trust domain, step-ca), worked-example pointer to examples/full-stack/, v0.10 forward limitations (socket activation, transactional update) High v0.10 P2 Complete
REQ-094 examples/full-stack/ directory with 5 valid jobspecs (web-app.md, api.md, worker.md, log-shipper.md, postgres.md) exercising ports/service/health/restart/update/constraints/affinity/lifecycle/task-groups/volumes/replication/DaemonSet; rendered/ subdir showing the Traefik dynamic YAML + systemd units orca generates; README.md walkthrough (init → node join → capacity set → ns create → job run → list --watch → inspect rendered) High v0.10 P3 Complete
REQ-095 README.md refresh: status line (v0.9 complete, v0.10 in progress), install --version example updated to current tag, subcommand table expanded to all commands with deprecation markers, update-in-place example updated, development targets complete (verify-reqs, security-scan, test-race, changelog), new Documentation + Examples sections linking all docs/*.md and examples/ High v0.10 P4 Complete
REQ-096 docs/namespace.md v0.9 multi-namespace layout update: replace v0.8 flat path table with v0.9 layout (cluster/, _defaults/, per-ns db/jobs/alloc/ns.md), ORCA_HOME/--system resolution, orca ns subcommand cross-link, v0.8 flat layout flagged deprecated Medium v0.10 P4 Complete
REQ-097 scripts/release.sh release pipeline fix: cross-build linux-amd64 tarball regardless of host arch (GOOS=linux GOARCH=amd64 go build); post-create asset verification (query /releases/tags/$VERSION, assert the tarball in attachments, retry/fail loudly if missing). Guarantees every Gitea release carries the Linux binary asset (root cause of v0.4.5 install) High v0.10 P1 Complete
REQ-098 scripts/install.sh asset fallback walk: if the latest/pinned release lacks the matching orca-<ver>-<os>-<arch>.tar.gz, walk backward through /releases?limit=20 to the most recent release that has it, with a clear warning. Keeps pulling from releases (not main). Optional --check dry-run mode High v0.10 P1 Complete

v0.11 Production Hardening Milestone Requirements

The following requirements (REQ-099…REQ-NN) are scoped to the v0.11 production-hardening milestone. They cover the ingress hybrid default (R-017), drift detection (R-018/R-019/R-020), the systemd Path unit implementation (D-227…D-237), and five net-new CLI commands added per operator decision Q2=C.

Ingress hybrid (R-017, D-215…D-226)

ID Requirement Priority Phase Status
REQ-099 internal/emitter/nft.go: nftables emitter renders /etc/nftables.d/orca.nft with DNAT (:443127.0.0.1:8443, :80127.0.0.1:8080), SYN-flood tcp-flags filter, ora_rl rate-limit meter (default 100/s burst 200), orca_trusted_probes set; idempotent nft -f apply; atomic rule-set swap (R-017, D-217, D-218, D-222) High v0.11 P15.5 Complete
REQ-100 Traefik static config emitter update: entryPoints.websecure.address changes from :443 to 127.0.0.1:8443 (default); entryPoints.web.address changes to 127.0.0.1:8080; --public-binding=traefik-on-public-ip opt-out emits :443/:80 instead; certs/mTLS/dynamic config unchanged (R-017, D-220, D-216) High v0.11 P15.5 Complete
REQ-101 orca doctor nft: checks table inet orca-ingress exists, expected DNAT rules present, rate-limit meter present, /etc/nftables.d/orca.nft parses cleanly (nft -c -f), file hash matches latest applied txn; drift detection via hash comparison (R-018 critical_paths, D-221, D-226) High v0.11 P15.5 Complete
REQ-102 orca nft CLI: show [--peer], diff --against <txn-id>, doctor (alias for orca doctor nft), country block add <cc-list> (opt-in GeoIP), rate limit set --rate N/s; all Layer-5 orchestrators that SSH into peers and parse nft output (D-223, D-222) Medium v0.11 P15.5 Complete

Drift detection (R-018/R-019/R-020, D-227…D-237)

ID Requirement Priority Phase Status
REQ-103 internal/drift package: Detector interface (Watch, Aggregate, Remediate, Acknowledge), Event, Config, PathSpec, RemediationPolicy types; iter.Seq2[Event, error] per D-017; signal.NotifyContext per D-023 (R-018, D-236) High v0.11 P10 Complete
REQ-104 orca drift CLI tree: watch [--interval=2s] [--paths=...] [--json], show [--peer], acknowledge <peer> <path>, remediate <peer> <path> [--force], config show, config validate; uses iter.Seq2 + signal.NotifyContext (D-236) High v0.11 P10 Complete
REQ-105 systemd Path unit emitter: for each critical path, emit orca-drift-<name>.path (PathChanged=, RateLimitIntervalSec=1s, RateLimitBurst=5) + orca-drift-<name>.service (Type=oneshot, ExecStart=/usr/local/bin/orca-drift-notify.sh %f, User=orca, security hardening: NoNewPrivileges, ProtectSystem=strict); R-001-clean (R-018, D-227, D-228) High v0.11 P10 Complete
REQ-106 scripts/orca-drift-notify.sh: receives changed path as $1, computes sha256 (or "DELETED"), writes event JSON to /etc/orca/state/drift-events/<event-id>.json (event_id, ts, host, path, status, new_sha256, latest_txn, triggered_by); stateless, idempotent; flock for serialization (D-228) High v0.11 P10 Complete
REQ-107 scripts/orca-aggregate.sh extension: existing 10s aggregator cadence (C-11) now also rsyncs each peer's /etc/orca/state/drift-events/, validates event hashes against /etc/orca/state/applied/<txn>/manifest.json, triggers orca-remediate.sh for auto-remediable paths, consumes (deletes) event files on peers (D-229, D-237) High v0.11 P09 Complete
REQ-108 scripts/orca-remediate.sh: re-pushes latest applied txn's per-peer render tree via rsync, runs peer-side applier; 5-min cooldown per path applies ONLY on successful remediation (transient failures retry next tick); cooldown state at /etc/orca/state/remediation-cooldown/ (D-231, D-232 refined per CLARIFY C4) High v0.11 P10 Complete
REQ-109 Drift cadence config in config.md (kind: ClusterConfig): drift.polling.{enabled,default_interval,max_concurrent_peers}, drift.paths.{critical,standard,excluded} (each with systemd_path_unit, interval, paths list), drift.remediate.{auto,auto_paths,require_approval_paths,notify_on_remediation}; critical defaults: Traefik dynamic, nftables, sudoers, orca-alloc services; secrets + /run/orca/* + drift-events dir excluded (R-018, D-231, D-234) High v0.11 P10 Complete
REQ-110 Pre-flight consistency gate in applier: orca-pull.sh (C-09) refuses new txns if drift detected on the target peer/namespace; --force flag overrides; per-namespace scoping means a drifted peer in ns-A does not block ns-B (R-020, Q4=A) High v0.11 P10 Complete
REQ-111 orca system user on peers: peer-setup emits useradd -r orca (system account, no login shell); orca-drift-*.service runs as User=orca Group=orca; SSH key access to lead for aggregator; idempotent at peer setup (net-new operational requirement from doc 5) High v0.11 P10 Complete
REQ-112 NFS detection at peer setup: orca node join / peer-setup detects NFS mounts on orca state dirs; if /etc/orca is on NFS, systemd Path units are disabled for those paths and polling is the only detection; logs a warning (D-233) Medium v0.11 P10 Complete
REQ-113 orca job restart <name>: restarts an allocation to pick up EnvironmentFile drift; goes through normal allocation lifecycle (not file-level remediation); triggers on drift of /etc/orca/allocs/<id>/env (D-235) Medium v0.11 P10 Complete

Net-new CLI surface (Q2=C — all five commands added to v0.11)

ID Requirement Priority Phase Status
REQ-114 orca cluster rotate-lead: moves cluster CA + lead state to a new bare-Linux peer (R-003 enforces bare-Linux-only lead); workloads keep running (certs already distributed); SSH key rotation; idempotent (Q2=C, folds into P14b daemon cutover) High v0.11 P14b Complete
REQ-115 orca upgrade --to-vX: thin wrapper around install.sh + orca restore (binary upgrade only, not full cluster rolling upgrade); handles Traefik binding cutover from :443 to 127.0.0.1:8443 for existing v0.9/v0.10 clusters (R-017 migration path, CLARIFY C1, C2=a thin wrapper); full cluster-rolling-upgrade defers to v1.x (Q2=C) High v0.11 P14a Complete
REQ-116 orca job migrate <name> --to <node>: drain+reschedule composite (uses P05 drain + P06 alloc history); live-migrate with storage replication defers to v1.x (CLARIFY C3=a); idempotent (Q2=C) Medium v0.11 P05 Complete
REQ-117 orca logs --all-nodes --since 5m: aggregates journald logs across peers via SSH; uses P06 alloc-history cache DB; iter.Seq streaming per D-017; --since duration flag; --all-nodes fans out (Q2=C, folds into P06) Medium v0.11 P06 Complete
REQ-118 orca doctor mTLS: verifies trust chain (CA → server cert → workload SVIDs exist + not expired) AND live mTLS handshake probe to each peer (reuses P01 metrics endpoint + P01.5 SPIFFE spike infra); both chain verification + live probe (CLARIFY C5, Q2=C, folds into P15.5) High v0.11 P15.5 Complete

Scope notes

  • REQ-099…REQ-118 = 20 net-new requirements (REQ count grows 98→118).
  • No new phases added (Q3=A folds ingress into P15.5; Q2=C folds CLI commands into existing phases).
  • P09 expands (REQ-107 aggregator extension); P10 expands (REQ-103…REQ-113, the largest phase); P15.5 expands (REQ-099…REQ-102 ingress + REQ-118 mTLS doctor).
  • P05 gains REQ-116 (migrate); P06 gains REQ-117 (logs --all-nodes); P14a gains REQ-115 (upgrade); P14b gains REQ-114 (rotate-lead).

v0.12 Milestone Summary — Security Hardening (Zero-Trust Identity)

Status: in progress (Phase 0). 30 net-new requirements (REQ-119..REQ-148) derived from the v0.12 threat-model review (25 findings F1..F25) and the zero-trust identity model (R-021). See ROADMAP.md for the 29-phase plan (P0 + P01..P27 + P28 final) and RESEARCH_v0.12.md for the full threat model.

Wave A — Critical injection & traversal

ID Requirement Priority Phase Status
REQ-119 Command injection fix in internal/runtime/podman.go & wasm.go: shell-quote cmdStr via shellQuote in SSH exec interpolation (podman.go:57, wasm.go:39); add injection regression tests (bats + Go) covering ;, |, $(), backticks, newline injection (F3) High v0.12 P01 pending
REQ-120 Namespace path traversal fix: validateNamespaceName in internal/ns/ rejects .., /, leading -, null bytes, control chars in ns create/ns inherit/ns set-constraint; add fuzz test (F4) High v0.12 P02 pending
REQ-121 Txn apply path allowlist: apply.sh python heredoc validates every path in desired-state.json against a prefix allowlist (/etc/orca/, /etc/traefik/orca*, /etc/systemd/system/orca-*, /etc/nftables.d/orca*, /etc/syncthing/orca*); rejects otherwise; HMAC-signed manifest unchanged (F5) High v0.12 P03 pending

Wave B — Zero-trust identity

ID Requirement Priority Phase Status
REQ-122 ACL enforcement wiring: acl.Check invoked in daemon handlers (read/write/admin by route) and SSH-push applier (validates ORCA_OIDC_TOKEN env var against JWKS before applying any txn); deny-by-default enforced; actor recorded in audit (F1, foundational for REQ-145) High v0.12 P06 pending
REQ-123 Daemon auth hardening: mandatory mTLS (remove plaintext mode entirely); OIDC bearer accepted as second factor on human-facing endpoints; MaxBytesReader body limits; pprof loopback-only by default, refuse non-loopback without --pprof-allow-public confirmation (F6, F24) High v0.12 P09 pending
REQ-124 HTTP request body size limits: http.MaxBytesReader on all JSON-decoding handlers; MaxHeaderBytes set; rejects oversized bodies (F24) Medium v0.12 P09 pending
REQ-125 Audit log tamper-evidence: hash-chained entries (prev_hash = sha256(prev_row || payload)), HMAC-SHA256 under master key on the chain head; orca doctor audit verifies the chain; append-only enforcement via SQLite trigger blocking UPDATE/DELETE; actor field carries OIDC sub or SPIFFE SVID (F2) High v0.12 P10 pending
REQ-126 SVID chain validation: VerifySVID validates the full cert chain against the CA pool, not just the URI SAN; reject certs signed by unknown CAs even with correct URI (F9) High v0.12 P11 pending
REQ-127 Backup symlink validation: Restore rejects Linkname that's absolute, contains .., or points outside ORCA_HOME; add regression test with crafted tarball (F7) High v0.12 P12 pending
REQ-128 step-ca /tmp hardening: step ca certificate writes to 0600 temp under ClusterDir()/step-tmp/ (or TMPDIR override), not world-readable /tmp; cleanup in defer (F10) High v0.12 P13 pending
REQ-129 Master key rotation: orca secrets rotate-master re-encrypts all namespace secrets under a new master key; new master key re-sealed to OIDC as part of the same operation; --dry-run + atomic + automatic rollback to old sealed key on any ns failure; no passphrase (R-021) (F12) High v0.12 P14 pending
REQ-130 File-mode audit expansion: EnforceFileModes extended to SSH key, master key (sealed blob), server cert/key, known_hosts; orca doctor modes checks all; startup refuses to run on violation (F13) Medium v0.12 P15 pending
REQ-131 aggregate.sh JSON injection fix + drift-gate parse fix: replace printf interpolation with jq-based JSON construction (or Go-side aggregator emitting JSON); fix orca-pull.sh R-020 parsing to use jq instead of grep (F11, F18) High v0.12 P16 pending
REQ-132 install.sh checksum+GPG verification: release.sh publishes SHA256SUMS + SHA256SUMS.asc (GPG-signed) alongside tarball; install.sh verifies before tar -xzf; fail closed on mismatch (F14) High v0.12 P17 pending
REQ-133 nftables ruleset hardening: add conntrack bounds (ct state established,related accept), input default-deny on orca chain, drop invalid packets; orca doctor nft audits live ruleset against emitted one (F21) Medium v0.12 P18 pending
REQ-134 sudoers hardening: add NOEXEC to apt-get/dpkg (or remove if unused); orca doctor proxmox audits sudoers file against expected allowlist (F22) Medium v0.12 P19 pending
REQ-135 System user consistency: Proxmox bootstrap creates nologin system user (-r -s /usr/sbin/nologin), matching peer-setup; orca doctor flags inconsistency on existing peers; orca upgrade migrates (F23) Medium v0.12 P20 pending
REQ-136 SQLite file-mode + at-rest encryption: store.Open sets DB file mode 0600; optional --encrypt-db (CGO-free fallback per C-31: file-mode 0600 + documented threat if SQLCipher needs CGO); no CGO (F8) High v0.12 P21 pending
REQ-137 Migration safety: copyFile -> atomic temp+rename; migrateDBSchema runs in transaction with foreign_keys(ON); pre-migration backup step (uses internal/backup); document manual rollback; v0.11->v0.12 identity migration: orca upgrade refuses clusters using --password/bare-tokens without --accept-identity-migration (F19, C-34) High v0.12 P22 pending
REQ-138 Legacy CA/mTLS/daemon + step-ca password-provisioner deletion: remove internal/security/ca.go legacy CA, internal/transport/mtls.go deprecated path, daemon plaintext mode; migrate orca init/orca cert * to step-ca exclusively; certpaths (v0.8 layout) removed; delete step-ca --password-file provisioner (replaced by OIDC provisioner); gate: P06/P08/P09/P11 all shipped (F16) High v0.12 P23 pending
REQ-139 known_hosts tightening + transport hardening: Flock tightens pre-existing looser perms to 0600; classifyDialErr switched from substring to typed errors; add SSH-exec rate limiting (token bucket per peer) (F15, F25) Medium v0.12 P24 pending
REQ-140 Drift event authentication: drift events signed with per-peer HMAC key (derived from master key); aggregator rejects unsigned/forged events; orca-drift-notify.sh reads key from 0600 file owned by orca (F18) Medium v0.12 P25 pending
REQ-141 Security integration test suite: hermetic harness exercising injection, traversal, symlink, drift-forgery, audit-tamper, daemon-auth-negative, OIDC mock-IdP flow, ACL-with-OIDC-claims negative tests, unseal/seal, WebAuthn virtual-authenticator ceremony, password-removal regression (assert --password is rejected); gates in .coreci.yml validate (C-33) High v0.12 P26 pending
REQ-142 Zero-trust + OIDC + WebAuthn + threat-model docs: docs/threat-model.md (STRIDE + zero-trust model + OIDC data-flow), docs/oidc.md (configure your IdP, Dex offline quickstart, claim-to-namespace mapping), docs/webauthn.md (passkey registration, RP ID, secure context), docs/security-runbook.md (unseal/seal, master key rotation, incident response, sudoers audit, nft audit); README security section names "no orca credentials" as an invariant Medium v0.12 P27 pending
REQ-143 Final review + ship + audit: multi-persona review across all phases, ciagent-audit reconstruction test, milestone merge to main, tag v0.11.29 (= v0.12 milestone release per feature-milestone rule) High v0.12 P28 pending
REQ-144 OIDC client + bundled Dex: orca auth login/logout/status/init-idp; OIDC config block (oidc.issuer, client_id, client_secret, scopes); bundled Dex systemd unit + Traefik route on the lead; BYO external IdP override via oidc.issuer repoint; JWKS caching + refresh; token storage at ~/.orca/credentials.json (0600); --oidc flag on commands requiring identity; browser auth-code + PKCE + local loopback redirect; headless device-code fallback (D-238..D-247) High v0.12 P04 pending
REQ-145 ACL rewrite to OIDC claims: remove KindToken entirely; KindSpiffe stays for machine identity; new KindOidc maps sub+groups -> namespace permissions; acl.Check takes OIDC claims struct; deny-by-default enforced in daemon + SSH-push applier; acl.json mode tightened to 0600 (F1) High v0.12 P06 pending
REQ-146 Remove all password/token paths (breaking): delete --password/$ORCA_PROXMOX_PASSWORD from Proxmox join (replace with pre-staged-key-only or step ssh OIDC cert exchange); delete step-ca --password-file provisioner (migrate to OIDC provisioner); delete any bare-token CLI paths; documented in migration guide (R-021, C-34) High v0.12 P07 pending
REQ-147 Master key seal-to-OIDC + Shamir recovery: master key encrypted with key derived from OIDC token exchange at unseal; orca cluster unseal/seal; sealed blob at ClusterDir()/master.key.sealed (0600); raw key never on disk; Shamir 3-of-5 shards printed at seal time; recovery via --recovery + 3 shards; mTLS-only offline path derives seal key from cluster CA (D-241, C-35) High v0.12 P08 pending
REQ-148 WebAuthn connector for Dex (passkeys): orca-webauthn-connector (~300 LoC Go, go-webauthn); register/login ceremonies at /orca/webauthn/{register,login} behind Traefik; orca auth register browser flow; passkey storage SQLite ClusterDir()/webauthn-credentials.db (0600, public keys only); RP ID = cluster Traefik domain; secure context via step-ca cert; headless device-code fallback; virtual-authenticator integration tests (D-240, D-243, D-244, C-38) High v0.12 P05 pending

Scope notes (v0.12)

  • REQ-119..REQ-148 = 30 net-new requirements (REQ count grows 118 -> 148).
  • 29 phases (P0 + P01..P27 + P28 final); GRILL may split/merge.
  • P04 (OIDC+Dex) and P05 (WebAuthn) are the new feat phases; the rest are fix/chore/test/docs/refactor. Milestone type = feature (at least one feat).
  • Tags on v0.11.x patch line: v0.11.0 (P0) ... v0.11.29 (P28 final = v0.12 milestone release).
  • v1.0.0 production-ready tag stays deferred for post-v0.12 UAT (per v0.11 PRD).

Milestone v0.13: Production Hardening Round 2 + UAT Plan

Status: in progress (2026-08-07). v0.12 (Security Hardening) is COMPLETE; v0.13 is the final hardening round before the v1.0.0 production-ready tag. v1.0.0 is gated on the UAT signoff script (scripts/uat-signoff.sh) delivered by this milestone.

Wave A — Toolchain & injection hardening

ID Requirement Priority Phase Status
REQ-149 Go toolchain bump to 1.25.12+ (closes 24 stdlib vulns: archive/tar GO-2025-4014/GO-2026-4869, crypto/tls GO-2026-5856/GO-2025-4008, crypto/x509 GO-2026-5037/4947/4946/GO-2025-4175/4155/4013, net/http GO-2026-4918/GO-2025-4012, net/url GO-2026-4601/4341/GO-2025-4010, encoding/pem GO-2025-4009, os GO-2026-4602); govulncheck -show verbose triage of 6 imported third-party vulns; bump deps with reachable traces High v0.13 P01 pending
REQ-150 Input validation & injection hardening: (a) orca logs --job validate against ^[A-Za-z0-9_-]+$, use shellQuote not %q (critical: backtick RCE via SSH fanout); (b) pprof isLoopback(":6060") treat empty host as non-loopback/bind-all, reject unless explicit public-allow flag wired; remove phantom --pprof-allow-public references, make loopback-only a hard invariant; (c) backup restore tar-slip fix: use filepath.Rel(target, dest) containment check instead of HasPrefix(name, ".."); (d) orca txn rollback validate txn ID against ^T-[0-9a-f]{16}$; (e) orca nft diff --against validate txn ID before filepath.Join; (f) drain stopAlloc validate allocID against ^[A-Za-z0-9_-]+$ before systemctl stop; (g) cluster_compat shellQuote(first) for peer dir name; (h) runtime/podman.go use shellQuote(image) not %q; (i) nft TrustedProbes validate each entry with net.ParseIP/net.ParseCIDR; (j) sudoers: validate --proxmox-user/--proxmox-role against ^[a-z_][a-z0-9_-]{0,31}$; write to fixed /etc/sudoers.d/orca; shellQuote all pveum/useradd; validateSudoers check the actual file written; (k) nft country block add validate ^[A-Z]{2}$ Critical v0.13 P02 pending

Wave B — Scheduler wiring & jobspec parser (architectural)

ID Requirement Priority Phase Status
REQ-151 Scheduler/deployment wiring: wire internal/scheduler.Schedule() into orca job run — replace local exec.CommandContext path with: evaluate constraints/capacity/affinity via scheduler → render systemd units via internal/emitter → SSH-push to target via internal/sshpush; --target overrides scheduler selection; capacity enforced (reject job if no node fits); CEL constraints evaluated; affinity weighted scoring; systemd-analyze verify on rendered unit before deploy; job run without --target uses scheduler bin-packing across registered nodes Critical v0.13 P03 pending
REQ-152 jobspec parser fixes: add case "schedule": and case "timeout": to top-level switch in internal/jobspec/markdown.go (currently silently dropped); fix DaemonSet — parser must not default Count to 1 for DaemonSet (validator rejects Count!=0); DaemonSet schedule block actually parsed and stored; timeout: on Jobs parsed and enforced (kill after duration); restart: policy translated to systemd Restart=/StartLimitBurst in emitter; add job lint warnings for advisory-only fields (cron, health, update, affinity) with honest "not enforced in this version" message Critical v0.13 P03 pending

Wave C — Zero-trust enforcement wiring

ID Requirement Priority Phase Status
REQ-153 ACL enforcement + WebAuthn registration auth: (a) wire acl.Check into all 5 daemon handlers (dispatch/jobs/nodes/tasks/health) — extract OIDC sub/SPIFFE SVID from mTLS peer cert, check against ACL for namespace+verb, deny-by-default; (b) wire acl.Check into sshpush applier + txn apply path (validate ORCA_OIDC_TOKEN bearer against JWKS); (c) thread OIDC sub/SVID into audit actor field (replaces "cli"/"daemon"); (d) fix acl.json mode 0644→0600; (e) fix WebAuthn unauthenticated registration — /orca/webauthn/register requires existing authenticated session or admin bootstrap token; do not allow overwriting existing credentials without re-auth; (f) add flock on acl.json for concurrent grant/revoke Critical v0.13 P04 pending
REQ-154 Seal/audit CLI + chain race + key zeroing: (a) implement orca cluster seal/unseal (OIDC token exchange→unwrap master key→zeroed on shutdown; Shamir 3-of-5 shards printed at seal time; sealed blob at ClusterDir()/master.key.sealed 0600); (b) implement orca doctor audit (invokes AuditRepo.VerifyChain); (c) implement orca doctor modes (invokes EnforceFileModes across ORCA_HOME); (d) fix audit hash-chain race — Append uses BEGIN IMMEDIATE transaction; (e) fix secrets rotate-master to actually re-seal to OIDC; (f) zero master key / namespace keys / SVID private keys after use (defense-in-depth against pprof heap extraction) High v0.13 P05 pending
REQ-155 auth init-idp real + auth register: (a) implement orca auth init-idp — render Dex systemd unit + config template + Traefik dynamic route from internal/webauthn/ connector at https://<cluster>/orca/webauthn/{register,login}; RP ID = cluster Traefik domain (C-38); HTTPS secure context via step-ca cert; atomic deploy with rollback; (b) implement orca auth register (browser flow to WebAuthn registration endpoint); (c) loadOIDCConfig config-file loading (oidc.issuer in config, not flags-only); (d) orca doctor oidc health check High v0.13 P06 pending

Wave D — Concurrency, transport, migration safety

ID Requirement Priority Phase Status
REQ-156 Concurrency safety: (a) SQLite busy_timeout(5000) + SetMaxOpenConns(1) on all DSNs (store, cache, recovery, webauthn); (b) secrets file flock (concurrent secrets set on same ns no longer loses data); (c) upgrade lock file (refuse concurrent orca upgrade); (d) backup lock file; (e) cache invalidation by write commands (node join/leave, ns create/delete, job run/stop invalidate relevant cache class — read-after-write consistency); (f) Executor.Run mutex scope fix (hold only for DB inserts, not whole job duration); (g) ns create atomic dir+ns.md write; (h) writeCurrentLead atomic write; (i) consolidate 3 divergent writeAtomic impls onto security.WriteAtomic; (j) WebAuthn session stores guarded with sync.Mutex High v0.13 P07 pending
REQ-157 Transport & SSH safety: (a) replace substring matching in transport.IsTransient AND sshpush.isTransient with typed sentinels (errors.Is); (b) rotateSSHKeys 2-phase atomic swap (stage new key on all peers → atomic swap → verify → cleanup old); (c) known_hosts flock field actually read by dial() (TOFU callback uses new field, not v0.8 certpaths.KnownHostsPath()); (d) IPv6 net.JoinHostPort in proxmox SSH dial + drain splitHostPort; (e) explicit timeouts for all SSH commands (peer-setup, drift remediate/ack, txn rollback, job restart — use context.WithTimeout); (f) verifyCutover use security.ClientTLSConfig with orca CA pool; (g) OIDC callback server ReadHeaderTimeout: 5s; (h) root SIGINT/SIGTERM handler for non-watch commands (clean SSH session + temp file cleanup) High v0.13 P08 pending
REQ-158 Migration & operational safety: (a) migration transaction + torn-write fix — migrateDBSchema wraps ALTER TABLE in transaction; crash after os.Rename but before schema fixup is recoverable; (b) job stop real systemctl stop via SSH (matches job restart pattern; honest semantics); (c) DB retention/compaction for jobs/tasks/audit_log tables (retention policy + orca doctor db compaction check); (d) orca logs --lines cap + --since upper bound (prevent OOM from unbounded journalctl output); (e) cache DB mode 0600 (matches store.Open); (f) upgrade.go cutover backup-file + atomic-rename (replace direct sed -i) High v0.13 P09 pending

Wave E — Observability, docs, UAT

ID Requirement Priority Phase Status
REQ-159 Observability expansion: metrics add orca_jobs_by_state histogram, orca_drift_events_total counter, orca_ssh_errors_total counter, orca_txn_apply_total/orca_txn_rollback_total counters, orca_acl_denials_total counter, orca_audit_chain_head gauge; new docs/metrics.md with Prometheus scrape config; security headers middleware on daemon (X-Content-Type-Options, X-Frame-Options) Medium v0.13 P10 pending
REQ-160 Doc drift round 2: (a) README — update status banner (v0.12+v0.13 complete), latest tag, subcommand table (add auth/nft/peer-setup/secrets rotate-master), correct "mTLS by default" claim (SSH-push is canonical, mTLS deprecated), add missing docs to table; (b) docs/cli.md — complete rewrite covering all ~40 subcommands; (c) CHANGELOG regen; (d) help text fixes (job run HCL→markdown, job stop daemon→SSH-push); (e) docs/webauthn.md add auth register; (f) docs/namespace.md add inherit/set-constraint; (g) docs/install.md+docker.md update version refs; (h) docs/security-runbook.md match P05 reality; (i) fix verify-reqs bold-format regex (currently bypasses v0.12); (j) fix ROADMAP/REQUIREMENTS v0.12 status hygiene; (k) docs/security-scanning.md gosec.json; (l) internal/proxmox/bootstrap.go comments (password→key auth); (m) deprecate orca status stub; (n) make verify-docs target (cli.md ↔ orca --help consistency) High v0.13 P11 pending
REQ-161 --type linux SSH-join: implement NodeKindLinux path (reserved at model/node.go:29); new internal/linux/bootstrap.go mirroring Proxmox pattern — orca pubkey deploy → orca system user → drift-events dir → no PVE role; key-auth only (R-021); orca node join --type linux --host <ip> --ssh-user root --ssh-key <path>; peer-setup.go kept as documented fallback High v0.13 P12 pending
REQ-162 UAT plan: docs/uat.md — 3-host topology (lead Ubuntu 22.04 + pve01 Proxmox VE 8/9 + worker01 Ubuntu 22.04); step-by-step with exact commands (bootstrap→onboard Proxmox→onboard Ubuntu worker→capacity→namespace→deploy full stack→migrate between hosts→exercise every claim); claim matrix mapping ~35 feature claims to UAT steps; signoff procedure (run scripts/uat-signoff.sh, paste output) Critical v0.13 P12 pending
REQ-163 UAT signoff script: scripts/uat-signoff.sh — idempotent, set -euo pipefail, ~35 named assertions covering all feature claims; read + non-mutating only (doctor, list, --dry-run); exit 0 iff all pass; scripts/uat-smoke.sh — pure-CLI subset for CI validate (version, acl file mode, doctor modes, no-password grep, metrics shape); tests for both scripts Critical v0.13 P12 pending

Scope notes (v0.13)

  • REQ-149..REQ-163 = 15 net-new requirements (REQ count grows 148 -> 163).
  • 14 phases (P0 + P01..P12 + P13 final); "no limit on phases" per operator.
  • P03 (scheduler wiring) and P12 (--type linux + UAT) are the feat phases; the rest are fix/chore/test/docs/refactor. Milestone type = feature (at least one feat).
  • Tags on v0.12.x patch line: v0.12.0 (P0) ... v0.12.13 (P13 final = v0.13 milestone release).
  • v1.0.0 production-ready tag stays deferred for post-v0.13 UAT signoff (operator runs scripts/uat-signoff.sh, pastes output back).

Accepted residual risks (documented in threat-model, not fixed)

  • OIDC tokens plaintext at rest (0600) — sealing on every CLI invocation conflicts with "no orca binary on servers" model
  • HSTS on daemon — mTLS-only API, no browser-facing surface on daemon itself
  • DNS resolution timeout — bounded by net.Dialer{Timeout: 15s}
  • Temp file cleanup on SIGKILL — orphaned temp files, operator-visible, low impact
  • Flock timeout on NFS — stuck holder is rare; tryFlockEx exists if needed later
  • "WASM-first" pillar aspirational — document as "WASM runtime available, process is default"
  • arm64/armv7 release — D-193 deferred; install.sh detection is forward-looking
  • OIDC callback slowloris — loopback, short-lived, single CLI invocation