Commit Graph

58 Commits

Author SHA1 Message Date
Jon Chery 0424f8ce02 feat(init): interactive remote pre-staging via ssh-copy-id
orca init now interactively prompts for remote host addresses and runs
ssh-copy-id automatically (password prompt passes through to the
operator). This makes orca init the single entry point — no manual
pre-staging of SSH keys required.

- Interactive: enter host addresses (one per line, empty line to finish)
- ssh-copy-id deploys the orca public key to each host
- Skipped in --json mode (non-interactive)
- Idempotent: re-running init can stage additional hosts

Also fixed: install.sh defaults to /usr/local/bin (on PATH for all users).
Non-root without sudo falls back to ~/.local/bin + auto-adds to .bashrc.
2026-08-10 17:37:42 +00:00
Jon Chery d324939699 fix: PVE role/user idempotency + init pre-staging instructions
- createPVERole: use grep -qF + fallback to pveum role mod (was broken
  by single-quote-in-grep pattern: grep -q '^'OrcaOperator'')
- createPVEUser: same idempotency fix (grep -qF + fallback to mod)
- orca init: prints ssh-copy-id instructions with the orca public key
  path after generating the SSH keypair
- docs/uat.md: removed manual pre-staging (ssh-keygen, ssh-copy-id
  with operator key, host-key fingerprint pinning). orca init handles
  key generation; node join uses the orca key by default; TOFU is
  automatic. Updated node join examples to not pass --ssh-key or
  --host-key-fingerprint.

---ci---
project: orca
status: fix
---/ci---
2026-08-10 17:07:30 +00:00
Jon Chery 790109ea24 feat(D): capacity auto-discovery + partial updates + ACL debug + UX (REQ-168,169)
- node capacity set: partial updates (only set dimensions passed;
  read-modify-write on existing row)
- node capacity auto [percentage]: SSH to node, discover CPU (nproc),
  memory (/proc/meminfo), disk (df), multiply by percentage (default 75)
- ACL check --verbose: prints resolved ACLPath + all entries + identity
- job list UX: short 8-char IDs, NODE column, conditional EXIT (- for
  non-terminal statuses)

---ci---
project: orca
milestone: v0.12.18
phase: D
status: complete
requirements:
  covered: [168, 169]
---/ci---
2026-08-10 16:33:27 +00:00
Jon Chery 64cbbd543e feat(C): remote deployment correctness — PVE runtime, DB record, Traefik (REQ-166)
- deployRemote branches on runtime: pve-ct/pve-vm on proxmox nodes
  invoke runtime.Registry.Prepare+Start (creates LXC/VM via SSH);
  process runtime on linux nodes uses systemd emitter; process on
  proxmox is rejected with clear error
- deployRemote emits Traefik dynamic config when spec has ports
  (TraefikEmitter.Render + SSH-push to /etc/traefik/dynamic/)
- model.Job: added Node field so job list --json reports deployed node
- job run remote case: inserts model.Job + alloc_history after
  deployRemote succeeds (job list and job stop now work for remote)
- --target name lookup: legacy dispatcher tries NodeID match
- job list UX: short 8-char IDs, NODE column, conditional EXIT (- for
  non-terminal statuses)
- emitter/traefik.go: directory provider (was single file), pve-ct/pve-vm
  registered in RegisterTraefik
- runtime/pve.go: shellQuote for image, idempotent create check

---ci---
project: orca
milestone: v0.12.18
phase: C
status: complete
requirements:
  covered: [166]
---/ci---
2026-08-10 16:21:50 +00:00
Jon Chery 16440a89f2 feat(B): Traefik deployment to all nodes during init/join (REQ-165, REQ-167)
- internal/traefik/install.go: shared Traefik installer (download +
  systemd unit + dynamic dir). Default v3.3.0, configurable.
- orca init: installs Traefik on localhost (idempotent, non-fatal
  if offline)
- proxmox bootstrap: installs Traefik on PVE host + downloads LXC
  template (default ubuntu-24.04, --lxc-template flag)
- linux bootstrap: installs Traefik on worker
- emitter/traefik.go: directory provider (was single file);
  register pve-ct/pve-vm in RegisterTraefik
- --lxc-template flag on node join (default ubuntu-24.04)

---ci---
project: orca
milestone: v0.12.18
phase: B
status: complete
requirements:
  covered: [165, 167]
---/ci---
2026-08-10 16:09:48 +00:00
Jon Chery a6ceb13491 fix(A): bootstrap plumbing — init creates SSH key + known_hosts + master key (REQ-164)
Fixes UAT issues 1, 8, 9, 12C, 13:
- orca init: generates SSH keypair (GenerateOrLoadSSHKey), creates
  empty known_hosts (0600), generates master key (GenerateMasterKey +
  SaveMasterKey). All were missing from runInit — every downstream
  SSH/secrets/cluster operation failed on a fresh init.
- TOFUHostKeyCallbackPath: creates known_hosts file if it doesn't exist
  (defense-in-depth alongside init)
- Linux bootstrap: replaces buggy inline TOFU with
  proxmox.TOFUHostKeyCallbackPath (first-connect key capture works)
- --type flag help: includes "linux" (was "localhost or proxmox")
- doctor network: SSH exec probe (was HTTP /healthz to :8443 — no
  daemon in SSH-push model R-001)

---ci---
project: orca
milestone: v0.12.18
phase: A
status: complete
requirements:
  covered: [164]
---/ci---
2026-08-10 16:02:19 +00:00
Jon Chery 52e17aefbf feat(P12): --type linux SSH-join + UAT plan + signoff script (REQ-161..163)
--type linux (REQ-161):
- internal/linux/bootstrap.go: SSH bootstrap for generic Linux workers
  (orcas pubkey, system user, drift-events dir; no PVE role/sudoers)
- internal/cli/node.go: joinLinux function + --type linux dispatch
- peer-setup kept as documented fallback

UAT plan (REQ-162):
- docs/uat.md: 3-host topology (lead Ubuntu + pve01 Proxmox + worker01
  Ubuntu), 22 step-by-step commands, 35-claim matrix, Proxmox
  prerequisite + alternative 3xUbuntu path (C-48), signoff procedure

UAT signoff script (REQ-163, C-47):
- scripts/uat-signoff.sh: 35 idempotent read-only assertions, exit 0
  iff all pass. Includes 4 critical-path assertions: job deploys to
  remote, ACL deny-by-default, seal/unseal round-trip, OIDC health
- scripts/uat-smoke.sh: 13 CI-tested pure-CLI assertions for .coreci.yml

Tests: node join --type linux test, fingerprint test updated, smoke
test all 13 pass.

---ci---
project: orca
phase: 12
milestone: v0.13
status: complete
requirements:
  covered: [161, 162, 163]
---/ci---
2026-08-10 14:33:29 +00:00
Jon Chery b6dd86fdf3 docs(P11): doc drift round 2 — README, cli.md, CHANGELOG, verify-reqs (REQ-160)
- README: status banner v0.12+v0.13, latest tag v0.12.10, subcommand
  table expanded (auth/nft/peer-setup/secrets rotate-master), "mTLS by
  default" corrected to "SSH-push canonical", docs table updated
- docs/cli.md: complete rewrite (521->1465 lines), all ~40 subcommands
- CHANGELOG: regenerated from git log (v0.11.29..HEAD)
- help text: job run HCL->markdown, job stop daemon->SSH-push
- docs/security-runbook.md: expanded to match P05 reality (seal/unseal,
  doctor audit/modes/oidc, incident response)
- docs/webauthn.md: added auth register (P06)
- docs/namespace.md: added inherit + set-constraint
- internal/proxmox/bootstrap.go: comments password->key auth
- internal/cli/status.go: deprecation warning
- scripts/verify-docs.sh + make verify-docs: cli.md <-> orca --help
- cmd/verify-reqs/main.go: fix bold-format regex (was bypassing v0.12)
  + case-insensitive status matching
- .ciagent/REQUIREMENTS.md: v0.12 REQs marked complete
- .ciagent/ROADMAP.md: v0.12 bolded COMPLETE

---ci---
project: orca
phase: 11
milestone: v0.13
status: complete
requirements:
  covered: [160]
---/ci---
2026-08-10 14:18:27 +00:00
Jon Chery ed91d68fbf feat(P10): observability expansion — metrics + security headers (REQ-159)
New metrics:
- orca_jobs_running / orca_jobs_failed / orca_jobs_complete (gauges)
- orca_audit_chain_head (gauge, chain integrity)
- orca_drift_events_total, orca_ssh_errors_total (counters)
- orca_txn_apply_total, orca_txn_rollback_total (counters)
- orca_acl_denials_total (counter)

Security headers on metrics + healthz endpoints:
- X-Content-Type-Options: nosniff
- X-Frame-Options: DENY

New file: docs/metrics.md (Prometheus reference + scrape config)

---ci---
project: orca
phase: 10
milestone: v0.13
status: complete
requirements:
  covered: [159]
---/ci---
2026-08-10 13:44:04 +00:00
Jon Chery 531b36924c fix(P09): migration + operational safety — job stop, retention, logs cap (REQ-158)
- job stop: real systemctl stop via SSH (was DB-only soft stop)
  resolves node from alloc_history or --peer flag
- doctor db-retention: row count check for jobs/tasks/audit_log
  warns at 100k rows, suggests backup + cleanup
- logs --lines: cap at 50000 (default 1000); --since upper bound 7d
  prevents OOM from unbounded journalctl
- cache DB mode 0600 (was 0644; matches store.Open)
- upgrade cutover: backup file + atomic rename (was sed -i)
  rollback restores from backup on failure

Tests: job stop SSH, DB retention warning, logs lines cap, cache mode,
cutover backup-restore + atomic rename.

---ci---
project: orca
phase: 9
milestone: v0.13
status: complete
requirements:
  covered: [158]
---/ci---
2026-08-10 13:37:28 +00:00
Jon Chery 3a3ea74d76 fix(P08): transport + SSH safety — typed errors, IPv6, timeouts, signal (REQ-157)
- transport.IsTransient: typed sentinels (ErrTransient/ErrPermanent) +
  standard net.Error/io errors.Is; substring matching removed
- sshpush.isTransient: same typed-error classification
- rotateSSHKeys: 2-phase atomic swap (stage peers -> swap local ->
  verify -> cleanup old); no more partial-result window
- known_hosts: dial() reads stored field (was reading v0.8 path directly)
- IPv6: net.JoinHostPort in proxmox SSH dial + drain splitHostPort
- SSH timeouts: context.WithTimeout on peer-setup, drift, txn rollback,
  job restart (default 2m)
- verifyCutover: orca CA pool TLS config (was default http.Client)
- OIDC callback: ReadHeaderTimeout 5s (slowloris defense)
- root Execute: signal.NotifyContext for SIGINT/SIGTERM (clean exit
  for non-watch commands)

Tests: typed-error classification table, IPv6 JoinHostPort, signal
handler context cancellation.

---ci---
project: orca
phase: 8
milestone: v0.13
status: complete
requirements:
  covered: [157]
---/ci---
2026-08-10 13:11:07 +00:00
Jon Chery 0358efe95b fix(P07): concurrency safety — SQLite, flock, cache, atomic writes (REQ-156)
- SQLite busy_timeout(5000) + SetMaxOpenConns(1) on all 4 DSNs
- secrets file flock (concurrent set on same ns no longer loses data)
- upgrade lock file (refuse concurrent orca upgrade)
- backup lock file (refuse concurrent backup)
- cache invalidation by writes (read-after-write consistency)
- Executor.Run mutex scope fix (hold only for DB inserts)
- ns create/inherit/set-constraint atomic writeNSMdAtomic
- writeCurrentLead + rotateSSHKeys atomic
- consolidate 3 writeAtomic impls onto security.WriteAtomic
- WebAuthn session stores guarded with sync.Mutex

Tests: concurrent secrets set, upgrade lock rejection, cache
read-after-write, WebAuthn session thread-safety (pass under -race).

---ci---
project: orca
phase: 7
milestone: v0.13
status: complete
requirements:
  covered: [156]
---/ci---
2026-08-10 12:27:05 +00:00
Jon Chery 978334a4bc feat(P06): auth init-idp real + auth register + doctor oidc (REQ-155)
Implements the v0.12 R-021 load-bearing change's working IdP path:
- orca auth init-idp: renders Dex config + systemd unit + Traefik route
  (atomic deploy, RP ID from --rp-id, C-38)
- orca auth register: opens browser to WebAuthn registration page
- loadOIDCConfig: config-file loading (oidc block + cluster_domain),
  falls back to flags + env vars
- orca doctor oidc: health check (systemctl is-active + .well-known)
- config.go: OIDCConfig block + ClusterDomain field
- markdown.go: oidc block parsing in config frontmatter

---ci---
project: orca
phase: 6
milestone: v0.13
status: complete
requirements:
  covered: [155]
---/ci---
2026-08-10 11:55:01 +00:00
Jon Chery 9e832387c6 feat(P05): seal/audit CLI + chain race fix + key zeroing (REQ-154)
New CLI commands:
- orca cluster seal: OIDC/CA-derived seal + Shamir 3-of-5 shards
- orca cluster unseal: OIDC/CA unseal + --recovery Shamir path
- orca doctor audit: VerifyChain + chain head report
- orca doctor modes: EnforceFileModes across ORCA_HOME

Fixes:
- audit hash-chain race: Append uses BEGIN IMMEDIATE transaction
  (concurrent appends no longer corrupt tamper-evidence)
- secrets rotate-master: re-seals to OIDC on sealed clusters
  (was writing raw key, docstring claimed re-seal)
- key zeroing: ZeroKey helper + defer after master/namespace key use
  (defense-in-depth against pprof heap extraction)
- store.Open: busy_timeout(5000) pragma (concurrent writers wait)

Tests: 18 new test functions (seal round-trip, Shamir recovery, doctor
audit tamper detection, doctor modes 0644 rejection, concurrent append
chain integrity, rotate-master re-seal, key zeroing).

---ci---
project: orca
phase: 5
milestone: v0.13
status: complete
requirements:
  covered: [154]
---/ci---
2026-08-07 21:06:39 +00:00
Jon Chery 5232fcb808 fix(P04): wire ACL enforcement + WebAuthn reg auth + audit actor (REQ-153)
R-023: Zero-trust enforcement operationally wired.

ACL enforcement (C-45 staged rollout):
- acl.Check wired into all 5 daemon handlers (dispatch/jobs/nodes/tasks)
- health endpoints exempt (liveness probes not gated)
- ACL log-only mode default (config acl.enforce=false); enforce after
  bootstrap ACL verified
- sshpush auth: ORCA_OIDC_TOKEN validated against JWKS before apply
- txn apply: Authorize hook validates OIDC token before running pull
- acl.json mode 0600 (was 0644)
- flock on acl.json for concurrent grant/revoke
- bootstrap ACL: init grants cluster-admin to orca-admins group + SVID

Audit actor identity:
- currentActor reads OIDC sub from credentials.json (was hardcoded "cli")
- threaded through all audit.Record calls via context

WebAuthn registration auth:
- BeginRegistration/FinishRegistration require authenticated session
- fail-closed 401 when no authFunc configured

New files: internal/daemon/acl.go, internal/cli/authactor.go,
internal/engine/actor.go, internal/identity/authtoken.go,
internal/sshpush/auth.go, internal/txn/auth_test.go

---ci---
project: orca
phase: 4
milestone: v0.13
status: complete
requirements:
  covered: [153]
---/ci---
2026-08-07 20:33:39 +00:00
Jon Chery cf3d98eb2b feat(P03): wire scheduler into job run + fix jobspec parser (REQ-151, REQ-152)
R-022: orca job run now deploys to remote nodes via scheduler -> emitter
-> SSH-push. Local exec fallback only when no remote nodes registered.

jobspec parser (REQ-152):
- schedule: and timeout: now parsed (were silently dropped)
- DaemonSet Count no longer defaults to 1 (was breaking DaemonSet)
- restart: policy translated to systemd Restart=/StartLimitBurst
- job lint: advisory warnings for cron/health/update/affinity (honest)

scheduler wiring (REQ-151, C-44):
- new internal/cli/job_dispatch.go: dispatchDecision + deployRemote
- scheduler.Schedule evaluates constraints/capacity/affinity
- --target overrides scheduler (manual pinning)
- local fallback only when len(ready non-localhost nodes)==0
- C-44: SSH-push failure returns error (no silent local fallback)
- systemd-analyze verify on rendered unit before deploy

Tests: 22 new test functions covering scheduler, parser, C-44, local
fallback, target override, systemd-analyze skip, restart directives.

---ci---
project: orca
phase: 3
milestone: v0.13
status: complete
requirements:
  covered: [151, 152]
---/ci---
2026-08-07 19:59:31 +00:00
Jon Chery 4b70e31cf4 fix(P02): input validation + injection hardening — 11 vectors (REQ-150)
Critical fixes:
- logs --job: validate ^[A-Za-z0-9_-]+$ + shellQuote (was %q backtick RCE)
- pprof: isLoopback treats empty host as bind-all (was :6060 bypass)
- backup restore: filepath.Rel containment check (was tar-slip via a/../..)
- WebAuthn reg auth deferred to P04 (requires session infra)

High fixes:
- txn rollback/show/apply: validate ^T-[0-9a-f]{16}$ + shellQuote
- nft diff --against: validate txn ID before filepath.Join
- drain stopAlloc: validate allocID ^[A-Za-z0-9_-]+$
- cluster_compat: shellQuote peer dir name
- podman image: shellQuote (was %q backtick injection)
- nft TrustedProbes: net.ParseIP/CIDR validation + split v4/v6 sets
- sudoers: validate --proxmox-user/--proxmox-role ^[a-zA-Z_][a-zA-Z0-9_-]{0,31}$
  fixed path /etc/sudoers.d/orca; shellQuote pveum/useradd; validateSudoers
  checks actual file
- nft country block: validate ^[A-Z]{2}$ (was len==2 only)

New file: internal/cli/validate.go (shared validators + shellQuote)
All 38 Go test packages pass. go vet + gofmt clean.

---ci---
project: orca
phase: 2
milestone: v0.13
status: complete
requirements:
  covered: [150]
---/ci---
2026-08-07 19:28:01 +00:00
Jon Chery b0158c96e9 fix(P01): bump go toolchain to 1.25.12 + fix pre-existing test bugs (REQ-149)
Toolchain:
- go.mod: go 1.25.0 -> 1.25.12 (closes 24 stdlib vulns: archive/tar,
  crypto/tls, crypto/x509, net/http, net/url, encoding/pem, os)
- go mod tidy clean; make build + test + lint pass

Pre-existing test bugs fixed (surfaced by toolchain bump):
- acl_test.go: KindToken always denies (R-021); tests updated to KindOidc
- acl.go: parseIdentity defaults to KindOidc (was KindToken, making
  acl grant/check CLI path non-functional for non-spiffe identities)
- init_test.go: migration version updated to 0008 (was 0007, stale since v0.12)
- doctor.go: CertCA now checks CA cert exists (was only checking file modes,
  passing when no CA present)
- scenarios_test.go: ACL integration test uses KindOidc + acl.json 0600

---ci---
project: orca
phase: 1
milestone: v0.13
status: complete
requirements:
  covered: [149]
---/ci---
2026-08-07 19:07:17 +00:00
Jon Chery 50c4e910ed fix(P14): master key rotation (REQ-129, F12, C-30)
---ci---
project: orca
phase: 14
milestone: v0.12
status: execute
---/ci---

orca secrets rotate-master: generates new master key, re-encrypts all
namespace secrets under new key, saves new key. --dry-run reports
affected namespaces. Atomic per-namespace; automatic rollback to old
key on any failure (C-30). Fixed unused nsKey in get+list (pre-existing
vet issue). Build + vet + tests green.
2026-08-07 11:24:19 +00:00
Jon Chery 20523ac045 fix(P07): remove all password/token paths (REQ-146, R-021, C-34) -- BREAKING
---ci---
project: orca
phase: 7
milestone: v0.12
status: execute
---/ci---

R-021 invariant: no passwords, no Orca-issued tokens, no CA-key
passphrases anywhere in the system.

Removed:
- proxmox/bootstrap.go: ssh.Password auth -> ssh.PublicKeys (key-based).
  --password/ removed from node join; replaced
  with --ssh-key (default: orca SSH key). Pre-staged key required.
- stepca/stepca.go: --password-file /dev/stdin removed from Init and
  issueCert. Provisioner changed to 'orca-oidc' (OIDC provisioner).
- identity/spiffe.go: --password-file removed from MintSVID. Provisioner
  changed to 'orca-oidc'.

Tests: all proxmox, stepca, identity, cli tests updated + pass. 3 new
password-rejection regression tests. Fake SSH server gains
PublicKeyCallback. go vet clean. Full build green.
2026-08-07 11:12:18 +00:00
Jon Chery 5429da1f87 feat(P04): OIDC client + auth CLI (REQ-144, D-239, D-242, D-246)
---ci---
project: orca
phase: 4
milestone: v0.12
status: execute
---/ci---

internal/identity/oidc.go: OIDC client (provider discovery, JWKS,
auth-code+PKCE+local-loopback redirect flow, device-code headless
fallback, token verification, credentials store at ~/.orca/credentials.json
0600, refresh). VerifyIDTokenStatic for SSH-push applier.
internal/cli/auth.go: orca auth login/logout/status/init-idp commands.
Dependencies: github.com/coreos/go-oidc/v3, github.com/go-webauthn/webauthn
(pre-added for P05).
Bundled Dex deploy (init-idp) stubs to P05 (WebAuthn connector ships
the full systemd unit + Traefik route).
9 tests pass (5 identity + 4 CLI). go vet clean. Full build green.
2026-08-07 10:59:55 +00:00
Jon Chery 5d115fc4b7 fix(P02): namespace path traversal (REQ-120, F4)
---ci---
project: orca
phase: 2
milestone: v0.12
status: execute
---/ci---

Add ns.ValidateName rejecting .., /, \, leading -, null bytes,
control chars, spaces, >128 chars, and reserved 'cluster'. Wire into
ns create/delete/inspect/validate/inherit/set-constraint + --parent
flag. Fuzz test + 14 traversal regression tests. No namespace dir can
escape ORCA_HOME.
2026-08-07 10:55:18 +00:00
Jon Chery 8d1cdceb5c feat(P15.5): threat model (C-19) + ingress hybrid (R-017, REQ-099..102) + doctor mTLS (REQ-118)
Sub-wave 1: internal/emitter/nft.go (nftables emitter, DNAT :443→127.0.0.1:8443, rate-limit, SYN-flood filter); Traefik static config 127.0.0.1:8443 binding (D-220); orca doctor nft; orca nft CLI (show/diff/doctor/country-block/rate-limit).
Sub-wave 2: docs/threat-model.md (R-017 trust boundary, R-020 deadlock, D-234 secret exclusion, orca user blast radius, step-ca SPOF); orca doctor mTLS (chain verification + live handshake probe, C5).

---ci---
project: orca
phase: 15.5
milestone: v0.11
status: execute
---/ci---
2026-08-07 08:35:06 +00:00
Jon Chery c5048822e5 feat(P14b,P14c): daemon cutover + rotate-lead (REQ-114) + mixed-version tolerance (REQ-065, REQ-086, C-13)
P14b: orca cluster cutover (stop v0.8 daemon, adopt running allocs);
orca cluster rotate-lead --to (R-003 enforcement, CA+master key copy,
SSH key rotation). P14c: orca doctor no-orca-on-server (R-001
enforcement); orca cluster compat-check (mixed-version tolerance).

---ci---
project: orca
phase: 14b
milestone: v0.11
status: execute
---/ci---
2026-08-07 08:18:12 +00:00
Jon Chery 9a28dc907b feat(P14a): v0.8→v1.0 data migration (REQ-066, C-07) + orca upgrade (REQ-115, C-25, C-27)
internal/migration/migrate.go: Migratev08tov11 (flat→multi-ns, schema
migration, CA import to step-ca, config.hcl preserve). internal/cli/
upgrade.go: orca upgrade --to (thin wrapper, R-017 binding cutover
with C-25 post-verify+rollback, C-27 orca user creation, --import-ca,
--dry-run). Tests: detect/migrate/dry-run/idempotent, cutover verify/
rollback, user creation.

---ci---
project: orca
phase: 14a
milestone: v0.11
status: execute
---/ci---
2026-08-07 08:00:10 +00:00
Jon Chery 97b88a703c feat(P13): ns subcommands (inherit, set-constraint) + deprecation warnings (REQ-068)
orca ns inherit <name> --parent (cycle detection), ns set-constraint
<key>=value>. Deprecation warnings on orca cert ca-init/gen/renew
(step-ca replaces) and .hcl jobspec (R-013). --no-deprecation-warnings
suppresses all.

---ci---
project: orca
phase: 13
milestone: v0.11
status: execute
---/ci---
2026-08-07 07:47:28 +00:00
Jon Chery 020aa01623 feat(P11,P12): orca job lint (REQ-084) + orca job verify (dry-run txn)
P11: orca job lint <spec.md> — schema/CEL/body/migration/best-practice
checks; --explain, --format json; exit 0/1 by errors found.
P12: orca job verify <spec.md> — dry-run txn (render + stage + verify
without apply); reports planned allocs/files/units; no side effects;
--namespace, --json.

---ci---
project: orca
phase: 11
milestone: v0.11
status: execute
---/ci---
2026-08-07 07:29:44 +00:00
Jon Chery 03f3585f16 feat(P10b): drift detection (R-018/R-019/R-020, REQ-103..113)
internal/drift/drift.go: Detector (Watch via iter.Seq2, Aggregate,
Remediate with cooldown-on-success, Acknowledge), Config with tiered
cadence (critical 5s + Path units, standard 30s, default 60s).
internal/cli/drift.go: orca drift {show,watch,acknowledge,remediate,
config}. internal/emitter/drift_path.go: systemd Path+service unit
emitter (User=orca, ProtectSystem=strict). scripts/orca-drift-notify.sh
(sha256 event JSON), orca-remediate.sh (cooldown-on-success, transient
retry). Pre-flight gate (R-020, --force + per-ns scoping). orca
system user (REQ-111), NFS detection (D-233), orca job restart for
EnvironmentFile drift (D-235).

---ci---
project: orca
phase: 10b
milestone: v0.11
status: execute
---/ci---
2026-08-07 07:17:41 +00:00
Jon Chery 635e07e7a5 feat(P10a): transactional plane (REQ-075, REQ-079; C-09, C-23)
internal/txn/txn.go: Bundle (desired-state + apply/verify/rollback
scripts + signed manifest), RenderBundle (content-addressed txn-id),
Stage (SCP to lead), Apply (idempotent + rollback on failure).
scripts/orca-pull.sh: C-09 failure contract (idempotent, bounded
retry, deterministic, structured syslog) + C-23 (cluster-wide vs
ns-scoped --force distinction). internal/cli/txn.go: orca txn
apply/list/show/rollback CLI.

---ci---
project: orca
phase: 10a
milestone: v0.11
status: execute
---/ci---
2026-08-07 06:28:34 +00:00
Jon Chery 5cbe3020d3 feat(P09): collector + aggregator (C-11/C-12/C-14) + drift aggregation (REQ-107)
scripts/orca-aggregate.sh: 10s aggregator, cluster.json merge +
drift-events rsync + remediation trigger (P10b stub). scripts/orca-
watchdog.sh: C-11 starvation detection. internal/cli/collector.go:
orca collector start/stop/status. Tests: CLI + bats.

---ci---
project: orca
phase: 09
milestone: v0.11
status: execute
---/ci---
2026-08-07 06:14:16 +00:00
Jon Chery f530c9a3f7 feat(P07): recovery (orca restore) — verified restore + alloc protection
Extend restore with --dry-run (extract to temp, report, no write),
running-alloc protection (refuse without --force; stop+restart with
--force), post-restore verification (master key, namespaces, DBs),
audit log entry.

---ci---
project: orca
phase: 07
milestone: v0.11
status: execute
---/ci---
2026-08-07 05:47:01 +00:00
Jon Chery c8cf2e41e5 feat(P06): alloc history (REQ-071) + logs --all-nodes (REQ-117)
internal/store/alloc_history.go: AllocHistoryRepo (Record/List/Evict)
in orca_cache.db with 7-day TTL eviction goroutine. internal/cli/logs.go:
orca logs --all-nodes --since 5m with iter.Seq streaming, SSH fanout,
journalctl JSON parsing, signal.NotifyContext cancellation, --json output.

---ci---
project: orca
phase: 06
milestone: v0.11
status: execute
---/ci---
2026-08-07 05:36:30 +00:00
Jon Chery 41bcf0a6bf feat(P05): drain + daemon drain-and-stop (REQ-061) + job migrate (REQ-116)
orca node drain <host>: marks draining, stops allocs via SSH, marks
drained. orca daemon drain-and-stop: stops v0.8 daemons on peers.
orca job migrate <name> --to <node>: drain+reschedule composite
(C3=a, not live-migrate). Node states: draining, drained.

---ci---
project: orca
phase: 05
milestone: v0.11
status: execute
---/ci---
2026-08-07 05:17:01 +00:00
Jon Chery f61ef2aa9e feat(P04): backup/restore — signed tarball (HMAC-SHA256)
internal/backup/backup.go: Backup (tar.gz + HMAC-SHA256 signature,
excludes /run/orca + sockets + WAL/SHM), VerifySignature, Restore
(signature verify + extract + Force flag). internal/cli/backup.go:
orca backup --out + orca restore --in --force. Tests: round-trip,
signature mismatch, exclusion, force-refuse, force-overwrite.

---ci---
project: orca
phase: 04
milestone: v0.11
status: execute
---/ci---
2026-08-07 04:55:27 +00:00
Jon Chery 2e6436608f feat(P03): secrets subsystem (REQ-080) — AES-256-GCM + HKDF-SHA256 per-ns
internal/secrets/secrets.go: master key (0600), HKDF-SHA256 per-ns
derivation, AES-256-GCM per-line with AAD=line-number (anti-swap),
EncryptEnvFile/DecryptEnvFile, LoadCredential= map generation.
internal/cli/secrets.go: orca secrets set/get/list/rotate/delete.
Tests: round-trip, nonce uniqueness, AAD anti-swap, 0600 enforcement.

---ci---
project: orca
phase: 03
milestone: v0.11
status: execute
---/ci---
2026-08-07 04:47:33 +00:00
Jon Chery 33c2b4a78b feat(P02): ACL — SPIFFE + token identities, deny-by-default
internal/acl/acl.go: Identity, Permission, ACLEntry, ACL with
Grant/Revoke/Check/List; SpiffeNamespace extraction; deny-by-default.
internal/cli/acl.go: orca acl grant/revoke/list/check CLI;
state at cluster/acl.json. Tests: grant/revoke/deny/ns-isolation/concurrent.

---ci---
project: orca
phase: 02
milestone: v0.11
status: execute
---/ci---
2026-08-07 04:38:00 +00:00
Jon Chery cc53c1a3e4 feat(P01): metrics endpoint — hand-rolled Prometheus text exposition
internal/transport/metrics.go: Metrics struct with counters/gauges,
WritePrometheus text exposition; internal/cli/metrics.go: orca metrics
HTTP server on :9100 serving /metrics + /healthz. No client_golang dep.

---ci---
project: orca
phase: 01
milestone: v0.11
status: execute
---/ci---
2026-08-07 04:24:57 +00:00
Jon Chery b6d4db1a96 feat(P00): CLI cache layer (R-008) — orca_cache SQLite + cache CLI
internal/cache/ package with per-class TTLs (Get/Set/Invalidate);
wired into node/job/ns list read paths; orca cache show/invalidate CLI.
Tests: hit/miss/invalidate/TTL-expiry + bench <1ms hit.

---ci---
project: orca
phase: 00
milestone: v0.11
status: execute
---/ci---
2026-08-07 04:17:25 +00:00
Jon Chery 4c2e59cf3f fix(P06): workloadToTaskSpecs command split + runnable examples
Root cause: orca job run <example>.md failed with fork/exec: no such
file or directory on every example. Two compounding problems:

1. workloadToTaskSpecs (internal/cli/job.go:340) passed the entire
   runtime.command string (e.g. "/usr/bin/httpd -f /etc/orca/web-app/
   httpd.conf") as a single binary path to exec.Command, which then
   looked for a file literally named "/usr/bin/httpd -f ..." and
   failed. The v0.9 markdown parser stores command: as a raw string;
   the legacy HCL path had separate command+args fields. Fix: add
   splitCommand helper that splits on strings.Fields into binary+args,
   with /bin/true fallback for empty commands.

2. The example commands referenced binaries that don't exist on a bare
   Linux machine (/usr/bin/httpd, postgres, api-server, fluent-bit).
   Fix: rewrite the 5 example runtime.command values to use /bin/sleep
   3600 (long-running services) or /bin/echo (one-shot job) so they
   run out-of-the-box. Each file has a Production substitution note
   showing the real binary to use in deployment.

Verified: orca job run examples/full-stack/worker.md now succeeds
(exit 0). All 4 services (web-app, api, log-shipper, postgres) start
correctly (task started, pid assigned). 12 new unit tests pass
(splitCommand: 7 cases, workloadToTaskSpecs: 5 cases). All 5 example
jobspecs still parse + validate (gate C-20). make lint clean.

---ci---
project: orca
phase: 6
milestone: v0.10
status: execute
decisions:
  - id: D-195
    decision: split command string via strings.Fields in workloadToTaskSpecs
    rationale: exec.Command expects binary path + args as separate elements;
      the v0.9 markdown parser stores command: as a single string with no
      args field (unlike legacy HCL). strings.Fields is dep-free and handles
      multiple spaces/tabs. Shell quoting (single/double quotes inside the
      command) is not handled — examples avoid sh -c with quoted strings.
    confidence: 0.95
    alternatives: [shellquote.Split from mvdan/sh (adds dependency)]
lessons:
  - The v0.9 markdown jobspec path needs the same command+args split that
    the legacy HCL path had via separate command/args fields. The parser
    stores command: as a raw string; the CLI must split it before passing
    to exec.Command.
  - Example jobspecs should use /bin/sleep and /bin/echo (binaries that
    exist on every Linux machine) so they run out-of-the-box. Descriptive
    production commands belong in a comment block, not in runtime.command.
---/ci---
2026-08-05 21:23:32 +00:00
Jon Chery 667f20a7b3 feat(P0b): Markdown jobspec parser + dispatcher + fuzz harness (REQ-064,067)
P0b — Canonical Markdown+frontmatter jobspec parser (R-013/R-014).

Parser (internal/jobspec/markdown.go, REQ-064):
- WorkloadSpec/RuntimeBlock/PortSpec/VolumeSpec types. ParseMarkdown
  hand-rolled YAML frontmatter (no yaml.v3 dep). Kind validation (Job/
  Service/DaemonSet per R-012). BOM-stripped frontmatter, byte-exact body
  preservation (R-015) via the fuzz harness.

Dispatcher (internal/jobspec/dispatch.go, REQ-064):
- ParseFile/Dispatch routes on extension: .md->Markdown, .yaml/.yml->
  Markdown-with-empty-body, .hcl->ParseHCL adapter. HCL adapter converts
  Spec{Job,Tasks} to *WorkloadSpec (Kind=Job, Runtime.one_of=process).
  Backward compat preserved (REQ-090) — orca job run old-spec.hcl works.
- Legacy Parse renamed ParseHCLLegacy, marked // Deprecated per R-013.

Fuzz harness (internal/jobspec/markdown_fuzz_test.go, REQ-067, R-015):
- FuzzParseMarkdownRoundTrip with 10 seed corpus entries (CRLF, BOM,
  no-frontmatter, only-closing-separator, code-fence ---, trailing
  whitespace, empty body, etc). Asserts byte-exact body round-trip.

Tests: markdown_test.go (19 tests), dispatch_test.go (17 tests), fuzz
(10 seeds). jobspec package 89.2% coverage. cli 81.8% (no regression).

18 packages pass, 20 bats pass, gofmt clean, verify-reqs 90 consistent.

---ci---
project: orca
phase: P0b
milestone: v0.9
status: execute
---/ci---
2026-08-05 17:02:33 +00:00
Jon Chery 7bb31d4c09 feat(P0a2): namespace CRUD + inheritance engine (REQ-082)
P0a2 — Namespace inheritance resolver + orca ns CLI subcommands.

Resolver (REQ-082, internal/ns/resolve.go):
- Pure Resolve() function: DFS post-order chain assembly (most-specific
  first, _defaults implicit last D-185). Child-wins-scalar env merge, de-duped
  union constraints. Cycle detection with readable cycle path. Missing-parent
  + missing-_defaults + misordering (['_defaults','x']) rejection. Opt-out
  impossible (D-187). 89.6% coverage.

Parser (internal/ns/parse.go):
- ParseNSMd: hand-rolled YAML frontmatter (no yaml.v3 dep). Validates
  kind:Namespace + name, parses parents flow-array, inherits_env/secrets.
- ParseNSMdDir: walks root/*/ns.md, skips cluster/, requires _defaults.

CLI (internal/cli/ns.go, D-176):
- orca ns list/create/delete/inspect/validate. Inspect + validate use the
  resolver. Create refuses _defaults/cluster; delete refuses _defaults +
  non-empty namespaces. JSON output support. 85.2% coverage.
- Registered on rootCmd.

Tests: resolve_test.go (11 tests), parse_test.go (14 tests), ns_test.go
(21 tests). 18 packages pass, 20 bats pass, gofmt clean, verify-reqs 90
consistent.

---ci---
project: orca
phase: P0a2
milestone: v0.9
status: execute
---/ci---
2026-08-05 16:49:12 +00:00
Jon Chery fc94326b0e feat(P00): deprecation sweep + bash tooling gate + render contract + doc banners (v0.9 P00)
P00 — Re-architecture Foundation (deprecation/migration/test-infra/persona/docs).

Deprecation sweep (REQ-068, REQ-072, REQ-089):
- Add // Deprecated: doc comments to internal/daemon (R-001), internal/transport
  (REQ-073), internal/security/ca.go+csr.go (D-101/REQ-076), internal/engine/
  dispatcher.go+peer.go (CLI-side scheduler), internal/cli/daemon.go.
- orca daemon emits slog.Warn deprecation banner on every run (ungated); fires
  R-001 + v0.10-P05 drain-and-stop + v0.10-P14 deletion.
- orca cert and orca node join (mTLS path) emit deprecation warnings; proxmox
  SSH path (the v0.9 replacement) does not warn.
- Add --no-deprecation-warnings global flag on root command (PersistentPreRunE)
  for orca upgrade migrations.
- 12 new daemon/cert/node deprecation tests in internal/cli/daemon_test.go
  (cli coverage 81.9%, warnDeprecated 100%).
- Add DEPRECATED banners to v0.8 sections of ARCHITECTURE.md (verified the
  v0.9 supersession section + Supersession Table from prior turn are present).

Bash tooling gate (grill C-06, C-15, C-16, C-17, C-18):
- scripts/tests/test_helper.bash + example_test.bash — bats framework + helpers.
- scripts/lib/orca-log.sh — slog-compatible JSON logging to syslog (C-17).
- scripts/orca-verify-render.sh — render-contract validator skeleton (C-16).
- scripts/tests/orca-log_test.bash + orca-verify-render_test.bash — 20 bats
  tests total (happy + failure paths per C-15).
- .shellcheckrc — project shellcheck config.
- Makefile: test-bash + lint-bash targets (graceful skip if tools missing);
  wired into test + lint targets.
- internal/emit/contract.go + contract_test.go — versioned JSON render
  contract (orca.emit/v1) between Go emitters and bash appliers (C-16).
- .ciagent/BASH_CAPABILITY_MAP_v0.9.md — maps shipped internal/transport
  capabilities to bash-side equivalents or accepted drops (C-18).
- D-186 recorded in PROJECT.md: bash exempt from Go coverage gate; compensating
  control is bats + shellcheck + shfmt (C-06).

verify-reqs: 90 requirements consistent. Build/test/lint/fmt all green.
20 bats tests pass. Go tests pass. No v0.8 code deleted — only marked deprecated
(deletion deferred to v0.10-P14 per REQ-090 dual-write window).

---ci---
project: orca
phase: P00
milestone: v0.9
status: execute
---/ci---
2026-08-05 16:26:26 +00:00
Jon Chery 11da458883 test(cli): --host-key-fingerprint non-proxmox validation (T02.11, REQ-058)
---ci---
project: orca
phase: 2
milestone: v0.8
status: execute
---/ci---
2026-08-04 12:04:28 +00:00
Jon Chery 13e6762f0f feat(cli): orca node key-reset <node> — local known_hosts reset (T02.8, REQ-059)
---ci---
project: orca
phase: 2
milestone: v0.8
status: execute
---/ci---
2026-08-04 11:51:47 +00:00
Jon Chery 7cb12c52ce feat(proxmox): HostKeyFingerprint field on Options (T02.4, REQ-058)
---ci---
project: orca
phase: 2
milestone: v0.8
status: execute
---/ci---
2026-08-04 11:38:39 +00:00
Jon Chery 08481d35ce feat(cli): --host-key-fingerprint flag on node join (T02.3, REQ-058)
---ci---
project: orca
phase: 2
milestone: v0.8
status: execute
---/ci---
2026-08-04 11:37:01 +00:00
Jon Chery 6408342a7f test(cli): coverage uplift to ≥70% excl daemon.go (T01.6, REQ-057)
Add table-driven rootCmd.Execute() tests for the node, job, cert,
doctor, audit, status, version, and node-capacity subcommand families.
Each test runs against a temp ORCA_HOME and asserts stdout/stderr/exit
via the existing initTestEnv/resetRootFlags/discardWriter helpers
(RESEARCH §1.2). extend resetRootFlags to also reset the per-command
flag-bound globals so tests don't leak state between runs.

daemon.go is excluded from the ≥70% target (documented in node_test.go):
the daemon command starts a long-running mTLS server whose lifecycle is
covered by internal/daemon/server_test.go; only its --pprof flag
registration is verified here (daemon_test.go).

Coverage: go test -cover ./internal/cli → 76.2% overall (78.7% by
-func), which includes daemon.go's untested RunE; the non-daemon files
exceed 70% comfortably. go test -race PASS.

---ci---
project: orca
phase: 1
milestone: v0.8
status: execute
---/ci---
2026-08-04 01:43:38 +00:00
Jon Chery d7d6961261 fix(P05): final review fixes — PERSONAS.md body, config --addr precedence, migration 0007 dedup
Audit fix: PERSONAS.md body roster updated for v0.7 (was stale v0.6
content). Review P1-004: daemon --addr now uses cmd.Flags().Changed()
to detect explicit flag, so config listen_addr only applies when --addr
was not explicitly passed (correct flag>env>file>default precedence).
Review P1-001: migration 0007 now dedups existing duplicate serial_hex
rows before creating the UNIQUE index (backward-compat with v0.6 DBs
that accumulated duplicates before the constraint existed).

---ci---
project: orca
phase: 5
milestone: v0.7
status: execute
requirements:
  covered: [REQ-053, REQ-054, REQ-055, REQ-056]
  partial: []
---/ci---
2026-08-04 00:28:48 +00:00
Jon Chery 0b58286ca2 feat(P04): --pprof opt-in on orca daemon (REQ-056, I-308)
Separate *http.Server + *http.ServeMux (AD-024), default disabled.
Operator opts in via --pprof <addr>. WARN logged on startup. All pprof
handlers explicitly registered on dedicated mux (no DefaultServeMux
side-effect). I-308 deferred since v0.2 now implemented. 6 new tests.

---ci---
project: orca
phase: 4
milestone: v0.7
status: verify
requirements:
  covered: [REQ-056]
  partial: []
---/ci---
2026-08-04 00:22:17 +00:00
Jon Chery cd07e435d9 feat(P02): HCL config file parsing — internal/config package (REQ-054)
New internal/config package: Config struct (HCL tags), Load(paths...),
MergeOverrides(flags, env) with flag>env>file>default precedence (D-039).
No package-level state (AD-023). --config persistent flag on root command;
daemon uses cfg.ListenAddr when flag at default. 11 config tests + 2 cli tests.

---ci---
project: orca
phase: 2
milestone: v0.7
status: verify
requirements:
  covered: [REQ-054]
  partial: []
---/ci---
2026-08-04 00:09:33 +00:00