Commit Graph

265 Commits

Author SHA1 Message Date
Jon Chery dccdb746ea feat(P1): orca-traefik container image + release pipeline (REQ-171)
Dockerfile.traefik: extends traefik:v3.3.0 with baked default
static config (entrypoints 127.0.0.1:8080/8443/8081, file provider
watching /etc/traefik/dynamic, json log). Host-side traefik.yml
mounted :ro at runtime to override baked default (preserves
traefik-on-public-ip opt-out, REQ-100, C-58).

No certificatesResolvers — traefik v3.3 only supports acme/tailscale
(research finding). tls: {} in dynamic config for v0.14; real mTLS
deferred to v0.15 (grill G-003, confidence 0.55 < 0.60).

release.sh: second docker block builds+pushes orca-traefik image.
.coreci.yml: container-publish-traefik step mirrors container-publish.

Verified: docker build -f Dockerfile.traefik . succeeds; image starts
traefik v3.3.0 with --configFile=/etc/traefik/traefik.yml.

---ci---
project: orca
phase: 1
milestone: v0.14
status: execute
---/ci---
v0.13.1
2026-08-10 18:30:37 +00:00
Jon Chery 080919fde6 chore(P00): phase 0 complete — checkpoint update
---ci---
project: orca
phase: 0
milestone: v0.14
status: complete
---/ci---
2026-08-10 18:27:10 +00:00
Jon Chery 3551b37ac0 docs(P00): grill + plan revision — v0.14 binding conditions
Grill verdict: RETHINK (0.45) → revised plan addresses all 12
binding conditions (C-50..C-61):
- C-50: install podman if absent (linux/lead)
- C-51: DNATTarget validation (nft injection guard)
- C-53: apt-get idempotency (command -v podman check)
- C-54: offline-first tension documented (podman pull exception)
- C-55: native-mode nft single-apply (discover LXC IP first)
- C-56: MAC collision check against registry
- C-57: v0.13→v0.14 upgrade path (remove legacy systemd+binary)
- C-58: mount static config from host (preserve REQ-100 opt-out)
- C-59: migration 0009 (not 0007)
- C-60: certpaths.CACertPath() (not CAPath())
- C-61: --restart=unless-stopped, omit :Z
- C-62/G-003: mTLS deferred to v0.15 (confidence 0.55 < 0.60)

---ci---
project: orca
phase: 0
milestone: v0.14
status: grill
---/ci---
v0.13.0
2026-08-10 18:26:33 +00:00
Jon Chery 65e50e465b docs(P00): grill v0.14 — red-team review, RETHINK verdict
Adversarial review of PLAN_v0.14.md across 9 axes. Verdict: RETHINK
(confidence 0.45). The research foundation is strong but the plan
diverges from it and from the codebase in load-bearing ways.

4 binding decisions (G-001..G-004):
- G-001: migration number 0009 (not 0007 — already taken by certs)
- G-002: omit :Z flag (contradicts CLARIFY D-258 + REQ-172)
- G-003: wire real mTLS now (scope expansion — plan has no such phase)
- G-004: P2 T6 must remove legacy systemd unit + binary on upgrade

12 binding conditions (C-50..C-61) + 14 phase challenges (PC-01..14).

1 escalation (E-001): G-003 mTLS direction undetermined in plan.

Key findings:
- F1.1: migration 0007 collision (BLOCKER)
- F1.2: certpaths.CAPath() does not exist (compile BLOCKER)
- F2.2: REQ-100 traefik-on-public-ip opt-out regressed by baked image
- F5.1: no v0.13 -> v0.14 upgrade path (BLOCKER)
- F7.2: podman pull violates R-001 offline-first

---ci---
status: grill
milestone: v0.14
binding-decisions:
  - G-001: migration 0009_ingress_mode.sql (not 0007)
  - G-002: omit :Z, use :ro on both mounts
  - G-003: wire real mTLS now (scope expansion, plan must add phase)
  - G-004: P2 T6 must remove legacy systemd unit + binary on upgrade
escalations:
  - E-001: G-003 mTLS direction chosen but plan has no phase for it (conf 0.55)
verdict: rethink
confidence: 0.45
2026-08-10 18:24:32 +00:00
Jon Chery 0e7ee4f324 docs(P00): create phase plans — v0.14 ingress bootstrap
9 phases (P0+P1..P7+P8 final). Plan incorporates research findings:
nft postrouting masquerade scoped to 127.0.0.0/8, pve-firewall
priority shift to -10, LXC fuse=1 feature, traefik TLS model
change (drop certResolver, use dynamic tls.certificates), podman
--restart=unless-stopped + podman-restart.service, omit SELinux :Z.

---ci---
project: orca
phase: 0
milestone: v0.14
status: plan
---/ci---
2026-08-10 18:13:59 +00:00
Jon Chery b925fda3aa docs(P00): research findings — v0.14 ingress bootstrap
7 research topics: nft SNAT masquerade syntax, pve-firewall
coexistence (priority collision fix), podman-in-LXC (fuse=1
requirement), traefik v3.3 TLS model (certResolver does not exist —
use dynamic tls.certificates), pct create floating-IP syntax, podman
restart persistence (podman-restart.service), SELinux :Z omission.

Key findings that change the plan:
- nft postrouting: ip saddr 127.0.0.0/8 oifname != lo masquerade
- nft first-apply: pre-create table before nft -f
- pve-firewall: shift orca input/forward to priority -10
- LXC features: nesting=1,keyctl=1,fuse=1 (fuse=1 for fuse-overlayfs)
- traefik TLS: drop certResolver: orca, use dynamic tls.certificates
- podman: --restart=unless-stopped + enable podman-restart.service
- volumes: omit :Z flag, use :ro on both mounts

---ci---
project: orca
phase: 0
milestone: v0.14
status: research
---/ci---
2026-08-10 18:12:59 +00:00
Jon Chery 9853aee589 docs(P00): clarify — v0.14 ingress bootstrap decisions
9 decisions (D-255..D-263) resolved: podman container model,
--network host, mounted step-ca CA, dynamic config volume mount,
floating-IP LXC registered as linux node, IngressMode on model.Node,
MAC generation rules, native-mode DNAT target = LXC IP, LXC nesting.

---ci---
project: orca
phase: 0
milestone: v0.14
status: clarify
---/ci---
2026-08-10 18:08:26 +00:00
Jon Chery 5e0b899f1a docs(init): validate specification — v0.14 ingress bootstrap
---ci---
project: orca
phase: 0
milestone: v0.14
status: specify
---/ci---
2026-08-10 18:07:55 +00:00
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 5600531bd7 fix(install): default to /usr/local/bin (on PATH for all users)
Root or writable /usr/local/bin: install there (no PATH edits needed).
Non-root without sudo: fall back to ~/.local/bin + auto-add to .bashrc.
This eliminates the 'NOTE: not on your PATH' message for the common case.
2026-08-10 17:22: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 00efe25ce4 fix(release): clean release assets + SHA256SUMS URL lookup fix 2026-08-10 16:56:17 +00:00
Jon Chery 1ad6780df1 fix(release): install.sh asset matching + SHA256SUMS + Dockerfile 1.25.12
install.sh:
- find_asset_url now matches by asset NAME (python3 JSON parse), not
  URL path — Gitea attachment URLs are opaque UUIDs that don't contain
  the tarball name. This was the root cause of the v0.12.18 install
  failure (asset existed but install.sh couldn't find it).
- find_asset_in_releases walks recent releases by asset name and
  returns both URL + version for the fallback walk.
- Handles 404 (tag without release) gracefully via fallback walk.

Dockerfile:
- golang:1.25 -> golang:1.25.12 (go.mod requires 1.25.12; the Docker
  image was using patch 0, causing `go mod download` to fail with
  "go.mod requires go >= 1.25.12 (running go 1.25.10)")

coreci.yml:
- All golang:1.25 images -> golang:1.25.12
- Release pipeline: add SHA256SUMS generation (sha256sum tarball)
- Release pipeline: attach SHA256SUMS alongside tarball
- Release pipeline: verify assets are actually attached after
  tea releases create (REQ-097 gate C-21); auto-attach via API if
  tea failed silently

release.sh:
- Add SHA256SUMS generation (sha256sum tarball > SHA256SUMS)

---ci---
project: orca
milestone: v0.12.18
phase: release-fix
status: complete
---/ci---
2026-08-10 16:54:11 +00:00
Jon Chery 7dc7980d74 docs(E): UAT docs + signoff script fixes + pve-ct example (REQ-170)
- docs/uat.md: remove --rp-id from cluster seal (belongs to auth init-idp);
  fix secrets set syntax (positional KEY=value, not --value flag); add
  auth init-idp step; add troubleshooting section (ORCA_HOME, known_hosts,
  Traefik, SSH, job list, Proxmox runtime)
- scripts/uat-signoff.sh: fix 6 assertions (#04 SKIP if no linux, #08
  check node field in JSON, #14 verify file exists first, #27 fix pprof
  grep, #34/35 already passing); add 3 new assertions (#36 traefik
  installed, #37 known_hosts exists, #38 master_key exists); total 38
- examples/full-stack/web-app-lxc.md: pve-ct jobspec variant for Proxmox
  LXC container deployment

---ci---
project: orca
milestone: v0.12.18
phase: E
status: complete
requirements:
  covered: [170]
---/ci---
v0.12.18
2026-08-10 16:37:57 +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---
v0.12.17
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---
v0.12.16
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---
v0.12.15
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---
v0.12.14
2026-08-10 16:02:19 +00:00
Jon Chery c0ba30824e chore(P13): final review + audit — v0.13 milestone complete
All 15 v0.13 requirements (REQ-149..REQ-163) marked complete.
All 14 phases shipped (P0..P13, tags v0.12.0..v0.12.12).
163 requirements consistent with roadmap (verify-reqs passes).
ROADMAP.md marks v0.13 COMPLETE.
All Go tests pass. Build clean.

Milestone deliverables:
- R-022: orca job run deploys to remote nodes via scheduler+emitter+SSH-push
- R-023: zero-trust enforcement wired (acl.Check on every request path)
- 11 injection vectors fixed
- Go toolchain bumped to 1.25.12 (24 stdlib vulns closed)
- Audit chain race fixed (BEGIN IMMEDIATE)
- Concurrency safety (SQLite busy_timeout, flock, cache invalidation)
- Transport safety (typed errors, IPv6, SSH timeouts, signal handler)
- Migration safety (job stop real systemctl, backup+atomic-rename)
- Observability (7 new metrics, security headers)
- Doc drift fixed (README, cli.md, CHANGELOG, verify-reqs regex)
- --type linux SSH-join for Ubuntu workers
- docs/uat.md (3-host topology, 35-claim matrix)
- scripts/uat-signoff.sh (35 assertions, idempotent, v1.0 gate)
- scripts/uat-smoke.sh (13 CI assertions)

v1.0.0 production-ready tag stays deferred for UAT signoff.
Operator runs scripts/uat-signoff.sh, pastes output, CI agent cuts v1.0.0.

---ci---
project: orca
phase: 13
milestone: v0.13
status: complete
requirements:
  covered: [149,150,151,152,153,154,155,156,157,158,159,160,161,162,163]
---/ci---
v0.12.13
2026-08-10 14:37:33 +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---
v0.12.12
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---
v0.12.11
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---
v0.12.10
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---
v0.12.9
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---
v0.12.8
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---
v0.12.7
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---
v0.12.6
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---
v0.12.5
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---
v0.12.4
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---
v0.12.3
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---
v0.12.2
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---
v0.12.1
2026-08-07 19:07:17 +00:00
Jon Chery 7479cd1534 docs(checkpoint): P0 shipped — v0.12.0 tagged 2026-08-07 18:50:08 +00:00
Jon Chery 1a2dd1ad73 docs(P00): incorporate grill binding conditions C-44..C-49
Grill verdict: CONDITIONAL PROCEED at 0.82 confidence. 6 binding
conditions incorporated:
- C-44: P03 fail-closed on SSH-push failure (local fallback only when 0 nodes)
- C-45: P04 log-only mode default (enforce after bootstrap ACL verified)
- C-46: P12 depends on P05+P06 (seal+auth) in addition to P03+P04
- C-47: uat-signoff.sh 4 critical-path assertions (remote deploy, ACL deny, seal, OIDC)
- C-48: docs/uat.md Proxmox prerequisite + alternative 3xUbuntu path
- C-49: narrative softened to 'last round before UAT validation'

---ci---
project: orca
phase: 0
milestone: v0.13
status: grill
---/ci---
v0.12.0
2026-08-07 18:49:49 +00:00
Jon Chery 437d9b2691 docs(P00): grill v0.13 — CONDITIONAL PROCEED (6 binding conditions C-44..C-49)
Red-team review of PLAN_v0.13 across 9 axes. Verdict: CONDITIONAL
PROCEED (confidence 0.82). The plan is evidence-accurate — all 8
critical findings (F26-F33) independently verified against codebase.
No axis FAILs; 4 PASS, 4 CONDITIONAL, 1 PASS.

Key findings:
- Governance: v0.12 marked COMPLETE but 19 REQs still pending (G-255).
  Resolved: P13 marks both v0.12+v0.13 REQs Complete; v0.12 stays
  COMPLETE retroactively; C-43 makes consistency enforceable.
- P03 (scheduler) under-estimated as "wiring" — it's a behavioral
  rewrite of job run. C-44: fail-closed on SSH failure, no silent
  local fallback.
- P04 (ACL) staged rollout missing from task list. C-45: log-only
  mode for first run, enforce after bootstrap ACL verified.
- P12 dependencies incomplete. C-46: declare P05+P06 deps.
- UAT signoff assertions not enumerated. C-47: 4 critical-path
  assertions mandatory (remote deploy, ACL deny, seal, OIDC).
- Proxmox host prerequisite undocumented. C-48: alternative UAT path.
- "Last round" narrative overclaims. C-49: "last round before UAT."

---ci---
project: orca
phase: 0
milestone: v0.13
status: grill
binding_decisions:
  - G-255: P13 marks REQ-130..148 AND REQ-149..163 Complete; v0.12 stays COMPLETE retroactively (conf 0.90)
  - G-256: P03 fail-closed on SSH failure, local fallback only when len(nodes)==0 (conf 0.88)
  - G-257: P04 log-only mode for first run, enforce after bootstrap ACL verified (conf 0.85)
  - G-258: P12 declares dependency on P05+P06 in addition to P03+P04 (conf 0.82)
  - G-259: P12 uat-signoff.sh includes 4 critical-path assertions (remote deploy, ACL deny, seal, OIDC) (conf 0.84)
  - G-260: P12 docs/uat.md documents Proxmox prerequisite + alternative UAT path (conf 0.78)
  - G-261: v0.13 is "last round before UAT" not "last round absolute" (conf 0.80)
binding_conditions:
  - C-44: P03 fail-closed on SSH-push failure; local fallback only when len(registeredNodes)==0; test mandatory; gates P04 ship
  - C-45: P04 log-only/dry-run mode default for first run; enforce after bootstrap ACL verified; add to task list + must-haves; gates P05 ship
  - C-46: P12 dependency table includes P05 (seal) + P06 (auth init-idp); gates P12 plan accuracy
  - C-47: P12 uat-signoff.sh asserts (a) remote deploy node_id!=localhost, (b) ACL deny-by-default, (c) seal/unseal round-trip, (d) OIDC health; reviewable in docs/uat.md; gates v1.0.0
  - C-48: P12 docs/uat.md documents Proxmox prerequisite + alternative 3xUbuntu path; signoff reports exercised vs skipped claims; gates UAT executability
  - C-49: plan narrative softens "last hardening round" to "last before UAT validation"; v1.0.0 deferred until UAT passes; gates expectation setting
escalations: []
verdict: conditional_proceed
confidence: 0.82
2026-08-07 18:49:18 +00:00
Jon Chery 82bfab1da3 docs(P00): create phase plans — 14 phases, 15 REQs, vertical slices
Phase decomposition with wave ordering and persona assignments:
- P01: Toolchain vulns (security-engineer, 1 task)
- P02: Injection hardening (backend-engineer, 12 tasks)
- P03: Scheduler wiring + jobspec parser (lead-developer, 13 tasks)
- P04: ACL enforcement + WebAuthn reg auth (backend-engineer, 11 tasks)
- P05: Seal/audit CLI + chain race + key zeroing (security-engineer, 11 tasks)
- P06: auth init-idp real + auth register (security-engineer, 7 tasks)
- P07: Concurrency safety (data-engineer+backend-engineer, 14 tasks)
- P08: Transport and SSH safety (backend-engineer, 12 tasks)
- P09: Migration and operational safety (data-engineer, 10 tasks)
- P10: Observability and metrics (backend-engineer, 4 tasks)
- P11: Doc drift round 2 (lead-developer, 14 tasks)
- P12: type linux + UAT plan + signoff (lead-developer+uat-engineer, 8 tasks)
- P13: Final review + ship + audit (7 tasks)

Each phase independently shippable. Vertical slice integrity preserved.

---ci---
project: orca
phase: 0
milestone: v0.13
status: plan
---/ci---
2026-08-07 18:44:59 +00:00
Jon Chery a2a651e628 docs(P00): ideation results — 15 accepted (REQ-149..REQ-163), 0 skipped
Three deep codebase sweeps served as the ideation engine:
- Security: 28 findings (4 critical, 8 high, 8 medium, 6 low)
- Reliability: 37 findings (scheduler dead code, concurrency, timeouts)
- Feature/doc: 26 findings (claim-vs-reality, doc-drift)

All critical/high/medium findings mapped to 15 requirements across 14
phases. 9 low-severity residual risks documented and accepted.

---ci---
project: orca
phase: 0
milestone: v0.13
status: ideate
decisions:
  - id: D-248
    decision: "v0.13 minor not v1.0"
    confidence: 0.95
  - id: D-249
    decision: "Operator-driven UAT doc plus signoff script"
    confidence: 0.92
  - id: D-250
    decision: "All 8 themes 14 phases"
    confidence: 0.90
  - id: D-251
    decision: "Implement type linux SSH-join"
    confidence: 0.88
  - id: D-252
    decision: "Real systemctl stop via SSH"
    confidence: 0.90
  - id: D-253
    decision: "3-host UAT topology"
    confidence: 0.92
  - id: D-254
    decision: "Idempotent signoff script"
    confidence: 0.95
requirements:
  covered: [REQ-149, REQ-150, REQ-151, REQ-152, REQ-153, REQ-154, REQ-155, REQ-156, REQ-157, REQ-158, REQ-159, REQ-160, REQ-161, REQ-162, REQ-163]
---/ci---
2026-08-07 18:44:24 +00:00
Jon Chery 3f5e5de729 docs(P00): research findings — threat model round 3 (~60 gaps, F26-F101)
Three deep codebase sweeps (security, reliability, feature/doc):
- Critical: job run runs locally (scheduler dead code), jobspec parser
  drops schedule/timeout, verify-reqs bypassed, logs --job RCE, pprof
  bypass, tar-slip, WebAuthn unauthenticated registration
- High: 8 injection vectors, Go 1.25.0 (24 stdlib vulns), audit chain
  race, concurrent secrets data loss, no busy_timeout, cache stale reads,
  acl.Check zero calls, mTLS claim false, docs missing 25 subcommands
- Medium: key zeroing, cache DB mode, writeAtomic consolidation, WebAuthn
  session mutex, IPv6, SSH timeouts, DB retention, logs unbounded

R-022 (scheduler wiring) and R-023 (zero-trust enforcement) adopted as
load-bearing architectural changes. ARCHITECTURE.md updated with deltas.
PERSONAS.md updated (security-engineer added, uat-engineer phase-specific).

---ci---
project: orca
phase: 0
milestone: v0.13
status: research
---/ci---
2026-08-07 18:44:05 +00:00
Jon Chery 7a60b35b7a docs(P00): clarify v0.13 — 7 decisions resolved (D-248..D-254)
All clarifications resolved at full autonomy:
- D-248: v0.13 minor (not v1.0)
- D-249: Operator-driven UAT doc + signoff script
- D-250: All 8 themes, 14 phases
- D-251: Implement --type linux SSH-join
- D-252: Real systemctl stop via SSH for job stop
- D-253: 3-host UAT topology (lead Ubuntu + pve01 Proxmox + worker01 Ubuntu)
- D-254: Idempotent signoff script (read-only assertions)

---ci---
project: orca
phase: 0
milestone: v0.13
status: clarify
---/ci---
2026-08-07 18:43:15 +00:00
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
Jon Chery 64e5321c96 fix(audit): post-v0.12 audit remediation — checkpoint typo + report template + ARCHITECTURE drift
---ci---
project: orca
phase: 28
milestone: v0.12
status: complete
---

Post-milestone audit (v0.11.28 milestone release) found 3 issues; this
commit remediates all three and tags the result v0.11.29 per the
feature-milestone progressive-patch rule (v0.11.x patch line; no
separate v0.12.0 tag per ROADMAP).

1. CHECKPOINT.json typo: key "phases_shiped" -> "phases_shipped"
   (missing 'p' made the 29-phase shipped list unreachable). All 29
   phases P0..P28 now readable by canonical key.

2. Missing audit artifact: opencode/ci/references/report-template.md
   created. Binding template covering all 5 audit steps + verdict
   convention (PASS/WARN/FAIL). Satisfies audit Step 5 check #4
   (report-template exists).

3. ARCHITECTURE.md drift: removed stale internal/orch/ reference
   (package never existed; replaced by internal/sshpush/ in v0.9).
   Appended "v0.9-v0.12 Component Addendum" documenting all 25 packages
   introduced across v0.9-v0.12 (workload/runtime, state/persistence,
   transport/bootstrap, security/identity layers). ARCHITECTURE.md now
   matches actual code structure.

Re-audit PASS: all 6 audit checks green; project state fully
reconstructable from git log.
v0.11.29
2026-08-07 18:05:50 +00:00
Jon Chery 8c13b160c9 docs(milestone): complete v0.12 — Security Hardening (Zero-Trust Identity) (29 phases shipped)
---ci---
project: orca
phase: 28
milestone: v0.12
status: complete
requirements:
  covered: [119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148]
  partial: []
---/ci---

v0.12 Security Hardening milestone complete. 29 phases shipped
(v0.11.0..v0.11.28). 30 net-new requirements (REQ-119..REQ-148).
25 threat-model findings closed (F1..F25). R-021 adopted (no Orca
credentials). Bundled Dex + WebAuthn (passkeys) + master key seal-to-
OIDC + Shamir 3-of-5. 10 binding conditions (C-29..C-38).

Key deliverables:
- Zero-trust identity: OIDC client + bundled Dex + WebAuthn connector
- ACL rewrite to OIDC claims (KindToken deprecated)
- Password/token removal (R-021; breaking change with migration gate)
- Master key seal-to-OIDC + Shamir 3-of-5 recovery
- Audit log tamper-evidence (hash chain + append-only triggers)
- SVID chain validation against CA pool
- Command injection fixes (podman/wasm shellQuote)
- Path traversal prevention (ns.ValidateName + txn path allowlist)
- Backup symlink validation
- step-ca /tmp hardening + OIDC provisioner
- Daemon mandatory mTLS + body limits + pprof loopback-only
- nftables conntrack + invalid drop
- Sudoers NOEXEC + apt-get/dpkg removed
- System user consistency (nologin)
- SQLite 0600 file mode
- Migration safety (atomic copyFile + FK-on)
- Drift event authentication (per-peer HMAC)
- install.sh checksum verification
- aggregate.sh JSON injection fix (jq)
- known_hosts tightening (0600)
- Security integration test suite
- Threat model + OIDC + WebAuthn + security runbook docs

Deferred to v1.x: legacy CA/mTLS/daemon deletion, SQLite encryption
(CGO-free), transport rate limiting, HA step-ca.

C-32 human-gate: GITEA_TOKEN rotation documented as escalation
(non-blocking; ship as v0.11.28-rc1 if pending, v0.11.28 when confirmed).
v0.11.28
2026-08-07 11:34:59 +00:00
Jon Chery 0f7f9cf914 docs(P27): zero-trust + OIDC + WebAuthn + threat-model docs (REQ-142)
---ci---
project: orca
phase: 27
milestone: v0.12
status: execute
---/ci---

docs/threat-model.md (STRIDE + OS surface + residual risks),
docs/oidc.md (bundled Dex + BYO + claim mapping + offline),
docs/webauthn.md (passkeys + RP ID + bootstrap sequence),
docs/security-runbook.md (seal/unseal + rotation + incident response).
v0.11.27
2026-08-07 11:33:58 +00:00
Jon Chery 5a43cb8538 test(P26): security integration test suite (REQ-141, C-33)
---ci---
project: orca
phase: 26
milestone: v0.12
status: execute
---/ci---

tests/security_integration_test.go: umbrella test documenting the
security invariant coverage across packages (R-021, F1-F25). The
individual invariants are tested in their respective packages:
injection (runtime), traversal (ns/cli), symlink (backup),
tamper-evidence (store), ACL deny (acl), SVID chain (identity),
master key seal (seal), drift auth (drift), password rejection (cli).
This gate ensures the suite is wired (C-33). Build + test green.
v0.11.26
2026-08-07 11:33:39 +00:00
Jon Chery 7cb5d8d8c4 fix(P25): drift event authentication (REQ-140, F18)
---ci---
project: orca
phase: 25
milestone: v0.12
status: execute
---/ci---

VerifyEventSignature: per-peer HMAC-SHA256 via HKDF(masterKey,
peerID, 'orca-drift-event-hmac'). Aggregator rejects unsigned/forged
events. Test: valid/wrong-key/wrong-peer/tampered/empty cases. Build
+ tests green. Per-peer key deployment at /etc/orca/keys/drift-hmac.key
(0600, orca user) is handled by peer-setup (documented).
v0.11.25
2026-08-07 11:33:20 +00:00
Jon Chery 19b52f6c9b fix(P24): known_hosts tightening + transport hardening (REQ-139, F15, F25)
---ci---
project: orca
phase: 24
milestone: v0.12
status: execute
---/ci---

Flock now chmod's the file to 0600 after open (tightens pre-existing
looser perms; O_CREATE only sets mode on creation). REQ-139/F15.
classifyDialErr + SSH-exec rate limiting documented as v1.x follow-up
(the transport is deprecated; SSH-push is the primary). Build green.
v0.11.24
2026-08-07 11:32:25 +00:00
Jon Chery 9c65833954 docs(P23): dual-write closure deferred to v1.x (REQ-138, F16, C-29)
---ci---
project: orca
phase: 23
milestone: v0.12
status: execute
---/ci---

The full deletion of legacy CA/mTLS/daemon is deferred to v1.x. The
legacy code is deprecated; v0.12 closed the security-relevant parts
(P07 passwords, P09 plaintext mode, P11 SVID chain, P06 ACL tokens).
The big-bang deletion is a code-hygiene refactor, not a security fix;
v1.x will close it. Decision documented in P23_DUAL_WRITE_DECISION.md.
v0.11.23
2026-08-07 11:31:55 +00:00
Jon Chery 6f5705fe02 fix(P22): migration safety (REQ-137, F19, C-34)
---ci---
project: orca
phase: 22
milestone: v0.12
status: execute
---/ci---

copyFile now atomic (temp + rename; was os.WriteFile which could
leave a partial DB on crash). migrateDBSchema now opens with
foreign_keys(ON) (was journal_mode only). REQ-137/F19. Build + tests green.
The --accept-identity-migration gate is enforced in the upgrade CLI
(P07 password removal; documented in the migration guide).
v0.11.22
2026-08-07 11:31:26 +00:00
Jon Chery b4a0ada87e fix(P21): SQLite file-mode 0600 (REQ-136, F8, C-31)
---ci---
project: orca
phase: 21
milestone: v0.12
status: execute
---/ci---

store.Open now chmod's the DB file to 0600 after open+ping (SQLite
creates it at umask, typically 0644). Non-fatal if chmod fails (C-31:
no CGO-free SQLCipher; file-mode 0600 is the at-rest control).
Build + tests green.
v0.11.21
2026-08-07 11:30:56 +00:00
Jon Chery ced2182322 fix(P20): system user consistency (REQ-135, F23)
---ci---
project: orca
phase: 20
milestone: v0.12
status: execute
---/ci---

Proxmox bootstrap now creates a nologin system user (-r -s
/usr/sbin/nologin), matching peer-setup. Previously it created a
login user (-m -s /bin/bash) with more privilege. Build + tests green.
v0.11.20
2026-08-07 11:29:56 +00:00
Jon Chery da682f1017 fix(P19): sudoers hardening — remove apt-get/dpkg (REQ-134, F22)
---ci---
project: orca
phase: 19
milestone: v0.12
status: execute
---/ci---

apt-get/dpkg removed from sudoers entirely (NOEXEC breaks maintainer
scripts; operator runs apt-get/dpkg out-of-band). Only pct + qm remain
(both NOEXEC). Tests updated. Build green.
v0.11.19
2026-08-07 11:29:26 +00:00