docs(init): validate specification — v0.14 ingress bootstrap
---ci--- project: orca phase: 0 milestone: v0.14 status: specify ---/ci---
This commit is contained in:
+12
-21
@@ -1,26 +1,17 @@
|
|||||||
{
|
{
|
||||||
"phase": 13,
|
"phase": 0,
|
||||||
"stage": "complete",
|
"stage": "specify",
|
||||||
"milestone": "v0.13",
|
"milestone": "v0.14",
|
||||||
"milestone_slug": "production-hardening-2",
|
"milestone_slug": "ingress-bootstrap",
|
||||||
"phase_role": "final",
|
"phase_role": "pre_execution",
|
||||||
"attempts": 0,
|
"attempts": 0,
|
||||||
"updated_at": "2026-08-10T14:30:00Z",
|
"updated_at": "2026-08-10T18:00:00Z",
|
||||||
"milestone_complete": true,
|
"milestone_complete": false,
|
||||||
"previous_milestone": "v0.12",
|
"previous_milestone": "v0.13",
|
||||||
"phase_count": 14,
|
"phases_shipped": [],
|
||||||
"phases_shipped": ["P0","P1","P2","P3","P4","P5","P6","P7","P8","P9","P10","P11","P12","P13"],
|
"tags_shipped": [],
|
||||||
"tags_shipped": ["v0.12.0","v0.12.1","v0.12.2","v0.12.3","v0.12.4","v0.12.5","v0.12.6","v0.12.7","v0.12.8","v0.12.9","v0.12.10","v0.12.11","v0.12.12"],
|
|
||||||
"requirements": {
|
"requirements": {
|
||||||
"covered": [149,150,151,152,153,154,155,156,157,158,159,160,161,162,163],
|
"covered": [],
|
||||||
"partial": []
|
"partial": []
|
||||||
},
|
|
||||||
"binding_conditions": ["C-39","C-40","C-41","C-42","C-43","C-44","C-45","C-46","C-47","C-48","C-49"],
|
|
||||||
"load_bearing_rule": "R-022",
|
|
||||||
"next_milestone": "v1.0",
|
|
||||||
"ship": {
|
|
||||||
"tag": "v0.12.13",
|
|
||||||
"merged_to_milestone": true,
|
|
||||||
"milestone_release": "v0.13"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -707,6 +707,52 @@ v0.13 closes all critical/high/medium findings (15 new requirements,
|
|||||||
14 phases) and delivers the **UAT plan + signoff script** that gates
|
14 phases) and delivers the **UAT plan + signoff script** that gates
|
||||||
the v1.0.0 cut.
|
the v1.0.0 cut.
|
||||||
|
|
||||||
|
### v0.14 Milestone: Ingress Bootstrap Completeness
|
||||||
|
|
||||||
|
**Scope**: ensure that linux & proxmox types are properly bootstrapped
|
||||||
|
with traefik during cluster init or node join. All cluster endpoints are
|
||||||
|
provisioned as sockets (R-007); routing between jobs and services
|
||||||
|
depends on traefik being present and properly configured. v0.13 shipped
|
||||||
|
traefik binary + systemd unit + empty dynamic dir but never wrote the
|
||||||
|
static config nor applied nft rules — `orca-traefik.service` fails on a
|
||||||
|
fresh `orca init` and `orca doctor nft` FAILs. v0.14 replaces the
|
||||||
|
binary+systemd model with a **podman container** running a custom
|
||||||
|
`orca-traefik` image, and completes the nft SNAT+DNAT ingress stack on
|
||||||
|
every node type.
|
||||||
|
|
||||||
|
**New load-bearing rule**: **R-024** — Traefik runs exclusively as a
|
||||||
|
podman container, deployed from the `orca-traefik` image published per
|
||||||
|
release. Every orca-managed ingress surface bootstraps: nft DNAT
|
||||||
|
(`:443→127.0.0.1:8443`, `:80→127.0.0.1:8080`) + SNAT/MASQUERADE
|
||||||
|
postrouting + `podman run -d --restart=always --network host -v
|
||||||
|
/etc/traefik/dynamic:/etc/traefik/dynamic:Z -v
|
||||||
|
/etc/orca/step-ca-root.crt:/etc/orca/step-ca-root.crt:ro
|
||||||
|
git.cloudinit.dev/coreci/orca-traefik:<tag>`. No node joins without a
|
||||||
|
functional podman-traefik ingress data plane.
|
||||||
|
|
||||||
|
**Three topologies** (per operator constraints):
|
||||||
|
1. **Linux**: host → nft → `podman run orca-traefik` (host network)
|
||||||
|
2. **Proxmox Native**: host → nft → LXC (nesting=1) → `podman run orca-traefik`
|
||||||
|
3. **Proxmox Floating-IP**: LXC (owns floating IP) → nft (inside LXC) → `podman run orca-traefik`
|
||||||
|
|
||||||
|
**Milestone type**: feature (multiple `feat` phases). Tags on v0.13.x
|
||||||
|
patch line: `v0.13.0` (P0) ... `v0.13.8` (P8 final = v0.14 milestone
|
||||||
|
release). 9 phases, 9 net-new requirements (REQ-171..REQ-179).
|
||||||
|
|
||||||
|
### v0.14 Decisions (D-series, full autonomy)
|
||||||
|
|
||||||
|
| ID | Question | Decision | Rationale | Confidence |
|
||||||
|
|----|----------|----------|-----------|------------|
|
||||||
|
| D-255 | Traefik deployment model? | **Podman container from custom `orca-traefik` image** | Operator constraint: traefik always deployed as a container. Replaces v0.13 binary+systemd. Image bakes static config. | 0.95 |
|
||||||
|
| D-256 | Container network mode? | **`--network host`** | Binds 127.0.0.1:8080/8443 directly on host/LXC loopback; nft DNAT targets that. No port publishing complexity. | 0.92 |
|
||||||
|
| D-257 | TLS cert resolver in image? | **Mounted step-ca root CA, no ACME** | Offline-first (R-001), no external CA. `certificatesResolvers.orca.tls` references `/etc/orca/step-ca-root.crt` (volume-mounted). Graceful degradation if absent. | 0.90 |
|
||||||
|
| D-258 | Dynamic config volume? | **Mount `/etc/traefik/dynamic` from host** | Zero changes to existing `deployRemote` WriteFile path (`job_dispatch.go:243`). File provider watches it. | 0.95 |
|
||||||
|
| D-259 | Floating-IP mode: register PVE host too? | **Yes — PVE host as `proxmox` + ingress LXC as `linux`** | PVE host needed in registry for `pct`/`qm` workload dispatch. Both register. | 0.92 |
|
||||||
|
| D-260 | `--ingress-mode` persistence? | **Store `IngressMode` on `model.Node`** | `doctor ingress` needs to know which check path to run. Schema migration. | 0.90 |
|
||||||
|
| D-261 | MAC generation when `--mac` omitted? | **Generate random `02:XX:...` in interactive mode; require `--mac` in `--json` mode** | Interactive: generate + confirm. Non-interactive: explicit required (no silent generation). | 0.88 |
|
||||||
|
| D-262 | Proxmox native nft DNAT target? | **LXC bridge IP (not 127.0.0.1)** | LXC has its own network namespace; 127.0.0.1 on PVE host ≠ LXC loopback. `NftClusterConfig.DNATTarget` field (default `127.0.0.1:8443`; native mode = `<lxc-ip>:8443`). | 0.90 |
|
||||||
|
| D-263 | LXC podman requirements? | **`--features nesting=1,keyctl=1` + `apt-get install podman`** | Ubuntu 24.04 LXC template has no podman preinstalled. Nesting+keyctl required for podman in unprivileged LXC. | 0.88 |
|
||||||
|
|
||||||
### v0.13 Decisions (D-series, full autonomy)
|
### v0.13 Decisions (D-series, full autonomy)
|
||||||
|
|
||||||
| ID | Question | Decision | Rationale | Confidence |
|
| ID | Question | Decision | Rationale | Confidence |
|
||||||
|
|||||||
@@ -362,7 +362,7 @@ production-ready tag. v1.0.0 is gated on the UAT signoff script
|
|||||||
- 14 phases (P0 + P01..P12 + P13 final); "no limit on phases" per operator.
|
- 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`).
|
- 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).
|
- 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).
|
- v1.0.0 production-ready tag stays deferred for post-v0.13 UAT signoff (operator runs `scripts/uat-signoff.sh`, paste output back).
|
||||||
|
|
||||||
### Accepted residual risks (documented in threat-model, not fixed)
|
### Accepted residual risks (documented in threat-model, not fixed)
|
||||||
|
|
||||||
@@ -374,3 +374,54 @@ production-ready tag. v1.0.0 is gated on the UAT signoff script
|
|||||||
- "WASM-first" pillar aspirational — document as "WASM runtime available, process is default"
|
- "WASM-first" pillar aspirational — document as "WASM runtime available, process is default"
|
||||||
- arm64/armv7 release — D-193 deferred; install.sh detection is forward-looking
|
- arm64/armv7 release — D-193 deferred; install.sh detection is forward-looking
|
||||||
- OIDC callback slowloris — loopback, short-lived, single CLI invocation
|
- OIDC callback slowloris — loopback, short-lived, single CLI invocation
|
||||||
|
|
||||||
|
## Milestone v0.14: Ingress Bootstrap Completeness
|
||||||
|
|
||||||
|
**Scope**: ensure that linux & proxmox types are properly bootstrapped with
|
||||||
|
traefik during cluster init or node join. All cluster endpoints are
|
||||||
|
provisioned as sockets (R-007); routing between jobs and services depends on
|
||||||
|
traefik being present on the host and properly configured. The v0.13 traefik
|
||||||
|
deployment shipped only a binary + systemd unit + empty dynamic dir — it
|
||||||
|
never wrote the static config nor applied nft rules, so `orca-traefik.service`
|
||||||
|
fails to start on a fresh `orca init` and `orca doctor nft` FAILs. v0.14
|
||||||
|
replaces the binary+systemd model with a **podman container** running a custom
|
||||||
|
`orca-traefik` image, and completes the nft SNAT+DNAT ingress stack on every
|
||||||
|
node type.
|
||||||
|
|
||||||
|
**New load-bearing rule**:
|
||||||
|
|
||||||
|
- **R-024** — Traefik runs exclusively as a podman container, deployed from
|
||||||
|
the `orca-traefik` image published per release. Every orca-managed ingress
|
||||||
|
surface bootstraps: nft DNAT (`:443→127.0.0.1:8443`,
|
||||||
|
`:80→127.0.0.1:8080`) + SNAT/MASQUERADE postrouting + `podman run -d
|
||||||
|
--restart=always --network host -v /etc/traefik/dynamic:/etc/traefik/dynamic:Z
|
||||||
|
-v /etc/orca/step-ca-root.crt:/etc/orca/step-ca-root.crt:ro
|
||||||
|
git.cloudinit.dev/coreci/orca-traefik:<tag>`. No node joins without a
|
||||||
|
functional podman-traefik ingress data plane. The image's baked static
|
||||||
|
config is canonical; host-side `traefik.yml` is no longer written.
|
||||||
|
|
||||||
|
**Three topologies** (per operator constraints):
|
||||||
|
|
||||||
|
- **Linux**: host → nft → `podman run orca-traefik` (host network)
|
||||||
|
- **Proxmox Native**: host → nft → LXC (nesting=1) → `podman run orca-traefik`
|
||||||
|
- **Proxmox Floating-IP**: LXC (owns floating IP) → nft (inside LXC) →
|
||||||
|
`podman run orca-traefik`
|
||||||
|
|
||||||
|
| ID | Requirement | Priority | Phase | Status |
|
||||||
|
|----|-------------|----------|-------|--------|
|
||||||
|
| REQ-171 | `Dockerfile.traefik` + release pipeline: build + publish `git.cloudinit.dev/coreci/orca-traefik:<version>` alongside the orca image per release; `.coreci.yml` `container-publish-traefik` step; image bakes static `traefik.yml` (entrypoints `websecure` 127.0.0.1:8443, `web` 127.0.0.1:8080, `traefik` 127.0.0.1:8081 + file provider watching `/etc/traefik/dynamic` + `certificatesResolvers.orca.tls` referencing mounted step-ca root CA at `/etc/orca/step-ca-root.crt` + json log/accessLog); graceful degradation if CA file absent | Critical | **v0.14 P1** | pending |
|
||||||
|
| REQ-172 | Replace `internal/traefik/install.go` binary+systemd install with a podman-container reconciler: `podman pull orca-traefik:<tag>` + `podman run -d --restart=always --network host --name orca-traefik -v /etc/traefik/dynamic:/etc/traefik/dynamic:Z -v /etc/orca/step-ca-root.crt:/etc/orca/step-ca-root.crt:ro <image>`; idempotent (pull+run if absent, start if stopped); works locally + over SSH-push; update all callsites (`init.go`, `linux/bootstrap.go`, `proxmox/bootstrap.go`, `upgrade.go`); remove systemd unit generation | Critical | **v0.14 P2** | pending |
|
||||||
|
| REQ-173 | nft SNAT+DNAT ruleset render+apply: extend `internal/emitter/nft.go` with postrouting masquerade chain; new `internal/ingress/bootstrap.go` renders `orca.nft` + applies `nft -f` + ensures `/etc/traefik/dynamic` dir + pushes step-ca root CA + invokes podman traefik reconciler; wired into `orca init` (localhost lead) | Critical | **v0.14 P3** | pending |
|
||||||
|
| REQ-174 | Remote ingress bootstrap via SSH-push for `orca node join --type linux`: push step-ca root CA, render+apply nft remotely, invoke podman traefik reconciler remotely; register node as `linux` | Critical | **v0.14 P4** | pending |
|
||||||
|
| REQ-175 | Proxmox native ingress mode (`--ingress-mode native`, default): on PVE host, render+apply nft (vmbr-compatible, separate `orca-ingress` table avoids pve-firewall conflict); create unprivileged LXC with `--features nesting=1,keyctl=1` running podman+orca-traefik; push step-ca root CA into LXC; nft DNAT target = LXC bridge IP; register PVE host as `proxmox` node; add `IngressMode` field to `model.Node` + schema migration | Critical | **v0.14 P5** | pending |
|
||||||
|
| REQ-176 | Proxmox floating-IP mode (`--ingress-mode floating-ip --floating-ip --gateway --mac [--net-prefix]`): `pct create` Ubuntu LXC named `ingress` with `net0 bridge=vmbr0,hwaddr=<mac>,ip=<floating-ip>/<prefix>,gw=<gateway> --features nesting=1,keyctl=1 --onboot 1`; install podman + run orca-traefik inside LXC; apply nft DNAT+SNAT inside LXC; register LXC as managed `linux` node (name=`ingress`, addr=`<floating-ip>:8443`); interactive prompt for params when flags absent + not `--json`; validate IP/MAC/gateway; PVE host also registered as `proxmox` for workload dispatch | Critical | **v0.14 P6** | pending |
|
||||||
|
| REQ-177 | `orca doctor ingress [--peer]`: verify orca-traefik container running (`podman inspect`), nft DNAT+SNAT applied, `/etc/traefik/dynamic` exists, step-ca root CA mounted; extend `scripts/uat-signoff.sh` with ingress assertions (40 podman_traefik, 41 nft_dnat_snat, 42 linux_worker, 43 proxmox_native_lxc / floating_ip_lxc) | High | **v0.14 P7** | pending |
|
||||||
|
| REQ-178 | Docs: `docs/cli.md` (`--ingress-mode` + floating-IP flags + `doctor ingress`), `docs/uat.md` (native + floating-IP topologies), `docs/ingress.md` (podman-traefik image + volume mounts + certResolver), `docs/docker.md` (orca-traefik image), `ARCHITECTURE.md` (R-024 + ingress bootstrap section) | High | **v0.14 P7** | pending |
|
||||||
|
| REQ-179 | Integration tests: hermetic harness fakes SSH; asserts init→podman traefik running + nft applied; linux join→remote podman+nft; proxmox native→LXC created with nesting + podman traefik; floating-ip→`pct create` with correct net0 args + LXC registered as linux node; release.sh builds orca-traefik image (Dockerfile.traefik parses) | Critical | **v0.14 P7** | pending |
|
||||||
|
|
||||||
|
### Scope notes (v0.14)
|
||||||
|
|
||||||
|
- REQ-171..REQ-179 = 9 net-new requirements (REQ count grows 163 -> 172).
|
||||||
|
- 9 phases (P0 + P1..P7 + P8 final); feature milestone (multiple `feat` phases).
|
||||||
|
- Tags on v0.13.x patch line: `v0.13.0` (P0) ... `v0.13.8` (P8 final = v0.14 milestone release).
|
||||||
|
- Milestone branch: `milestone/v0.14-ingress-bootstrap`.
|
||||||
|
|||||||
@@ -675,3 +675,57 @@ CI agent verifies and cuts v1.0.0).
|
|||||||
- jobspec `health` prober (v0.13 adds lint warning; enforcement deferred)
|
- jobspec `health` prober (v0.13 adds lint warning; enforcement deferred)
|
||||||
- jobspec `update` rolling/canary controller (v0.13 adds lint warning; enforcement deferred)
|
- jobspec `update` rolling/canary controller (v0.13 adds lint warning; enforcement deferred)
|
||||||
- jobspec `schedule.cron` scheduler loop (v0.13 adds lint warning; enforcement deferred)
|
- jobspec `schedule.cron` scheduler loop (v0.13 adds lint warning; enforcement deferred)
|
||||||
|
|
||||||
|
## Milestone v0.14: Ingress Bootstrap Completeness — **IN PROGRESS**
|
||||||
|
|
||||||
|
**Scope**: ensure that linux & proxmox types are properly bootstrapped with
|
||||||
|
traefik during cluster init or node join. All cluster endpoints are
|
||||||
|
provisioned as sockets (R-007); routing between jobs and services depends on
|
||||||
|
traefik being present and properly configured. v0.13 shipped traefik binary +
|
||||||
|
systemd unit + empty dynamic dir but never wrote the static config nor applied
|
||||||
|
nft rules. v0.14 replaces the binary+systemd model with a **podman container**
|
||||||
|
running a custom `orca-traefik` image, and completes the nft SNAT+DNAT ingress
|
||||||
|
stack on every node type.
|
||||||
|
|
||||||
|
**New load-bearing rule**: **R-024** — Traefik runs exclusively as a podman
|
||||||
|
container from the `orca-traefik` image published per release. Every
|
||||||
|
orca-managed ingress surface bootstraps nft DNAT + SNAT/MASQUERADE +
|
||||||
|
`podman run --restart=always --network host` with dynamic-config + step-ca
|
||||||
|
root CA volume mounts.
|
||||||
|
|
||||||
|
**Three topologies**:
|
||||||
|
1. **Linux**: host → nft → `podman run orca-traefik` (host network)
|
||||||
|
2. **Proxmox Native**: host → nft → LXC (nesting=1) → `podman run orca-traefik`
|
||||||
|
3. **Proxmox Floating-IP**: LXC (owns floating IP) → nft (inside LXC) → `podman run orca-traefik`
|
||||||
|
|
||||||
|
**Milestone type**: feature (multiple `feat` phases). Tags on v0.13.x patch
|
||||||
|
line: `v0.13.0` (P0) ... `v0.13.8` (P8 final = v0.14 milestone release).
|
||||||
|
|
||||||
|
- [ ] Phase 0: Pre-execution (SPECIFY→CLARIFY→RESEARCH→PLAN→GRILL) — tag `v0.13.0`
|
||||||
|
- [ ] Phase 1: `orca-traefik` container image + release pipeline (REQ-171) — tag `v0.13.1`
|
||||||
|
- [ ] Phase 2: Podman traefik reconciler — replace binary+systemd install (REQ-172) — tag `v0.13.2`
|
||||||
|
- [ ] Phase 3: nft SNAT+DNAT + `orca init` ingress bootstrap (REQ-173) — tag `v0.13.3`
|
||||||
|
- [ ] Phase 4: `orca node join --type linux` remote ingress bootstrap (REQ-174) — tag `v0.13.4`
|
||||||
|
- [ ] Phase 5: Proxmox native ingress mode — LXC + podman traefik (REQ-175) — tag `v0.13.5`
|
||||||
|
- [ ] Phase 6: Proxmox floating-IP LXC ingress + interactive prompt (REQ-176) — tag `v0.13.6`
|
||||||
|
- [ ] Phase 7: `doctor ingress` + docs + integration tests (REQ-177,178,179) — tag `v0.13.7`
|
||||||
|
- [ ] Phase 8: Final review + ship + audit (milestone release) — tag `v0.13.8` = **v0.14 milestone release**
|
||||||
|
|
||||||
|
### Per-phase REQ coverage (v0.14)
|
||||||
|
|
||||||
|
- **P1** — `Dockerfile.traefik` + release pipeline (REQ-171)
|
||||||
|
- **P2** — Podman traefik reconciler (REQ-172)
|
||||||
|
- **P3** — nft SNAT+DNAT + init bootstrap (REQ-173)
|
||||||
|
- **P4** — Linux node join remote ingress (REQ-174)
|
||||||
|
- **P5** — Proxmox native ingress — LXC + podman (REQ-175)
|
||||||
|
- **P6** — Proxmox floating-IP LXC ingress (REQ-176)
|
||||||
|
- **P7** — doctor ingress + docs + tests (REQ-177,178,179)
|
||||||
|
- **P8** — Final review + ship + audit
|
||||||
|
|
||||||
|
### v0.14 is a continuation milestone, not a direction change
|
||||||
|
|
||||||
|
The vision ("minimalist, offline-first, CLI-first orchestration engine")
|
||||||
|
is unchanged. v0.14 completes the ingress bootstrap that v0.13 left
|
||||||
|
non-functional (binary installed but no config, no nft applied). The
|
||||||
|
podman-container model is the operator's constraint; the architecture's
|
||||||
|
socket+traefik routing design (R-007, R-017) is unchanged.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"slug": "orca",
|
"slug": "orca",
|
||||||
"name": "Orca",
|
"name": "Orca",
|
||||||
"description": "Offline/CLI-first orchestration engine (Orca) \u2014 Nomad-inspired, far simpler than Kubernetes",
|
"description": "Offline/CLI-first orchestration engine (Orca) \u2014 Nomad-inspired, far simpler than Kubernetes",
|
||||||
"milestone": "v0.13",
|
"milestone": "v0.14",
|
||||||
"phase": 0,
|
"phase": 0,
|
||||||
"milestone_type": "feature",
|
"milestone_type": "feature",
|
||||||
"default_branch": "main",
|
"default_branch": "main",
|
||||||
@@ -185,7 +185,8 @@
|
|||||||
"registry": "git.cloudinit.dev",
|
"registry": "git.cloudinit.dev",
|
||||||
"owner": "coreci",
|
"owner": "coreci",
|
||||||
"image": "orca",
|
"image": "orca",
|
||||||
"credential_env": "GITEA_TOKEN"
|
"credential_env": "GITEA_TOKEN",
|
||||||
|
"images": ["orca", "orca-traefik"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"secrets": {
|
"secrets": {
|
||||||
|
|||||||
Reference in New Issue
Block a user