Agent #2 of 3 for OpenovaFlow. Ships the Go backend independently of
Agent #1's TS packages (@openova/flow-core + @openova/flow-canvas);
the FlowMessage JSON contract is locked between agents.
Two Go modules (separate go.mod each so the dep graphs stay decoupled):
- products/openova-flow/server/ — stateless HTTP+SSE event router.
Map<flowId, RingBuffer<FlowMessage>>, in-memory, no DB. Endpoints:
POST /v1/flows/{flowId}/events, GET /v1/flows/{flowId}/snapshot,
GET /v1/flows/{flowId}/stream (SSE with 15s heartbeats + Last-Event-ID
seq stamping), DELETE /v1/flows/{flowId}, GET /healthz, /readyz.
Zero external Go deps (stdlib net/http). Ring cap default 4096
(env-overridable). Locked schema validation rejects unknown envelope
variants with 400.
- products/openova-flow/adapter-flux/ — DaemonSet sidecar that watches
helm.toolkit.fluxcd.io/v2.HelmRelease + HelmChart CRs via
client-go's dynamicinformer.NewFilteredDynamicSharedInformerFactory
(canonical seam: products/catalyst/bootstrap/api/internal/k8scache/factory.go),
maps each event to FlowMessage via a pure-transform mapper, POSTs to
the configured openova-flow-server with exponential-backoff retry.
Status mapping: Ready=True → succeeded, InstallFailed/UpgradeFailed/
RetriesExhausted → failed, Progressing/Unknown/other-False → running,
no Ready yet → pending. FlowNode.id format "{REGION_KEY}/{hrName}"
so multi-region renders correctly. Region-aware: synthetic region
parent FlowNode emitted on bootstrap; dependsOn entries fan-out to
finish-to-start relationships.
Two wrapper charts under platform/openova-flow-{server,emitter}/chart/
(canonical seam: platform/qa-app/chart/ for the simple
Deployment+Service+SA shape; platform/k8s-ws-proxy/chart/ for the
DaemonSet+ClusterRole+ClusterRoleBinding shape). MIRROR-EVERYTHING:
image refs go through harbor.openova.io/proxy-ghcr/openova-io/...
Image tag + required runtime config fail-fast at chart render via
_helpers.tpl so silent ImagePullBackOff / boot crash is impossible.
Two bootstrap-kit HRs added (slots 56 + 57):
- 56-bp-openova-flow-server (dependsOn: bp-cilium, bp-cert-manager) —
installs on primary cluster only; Cilium Gateway HTTPRoute at
openova-flow.<sovereignFQDN> for cross-cluster ingest.
- 57-bp-openova-flow-emitter (dependsOn: bp-flux) — DaemonSet, runs
on every cluster (mother + Sovereign + every secondary region).
scripts/expected-bootstrap-deps.yaml updated; check-bootstrap-deps.sh
audit passes (drift=0, cycles=0).
Tests (all green):
- server contract_test.go — every FlowMessage variant round-trips JSON,
unknown/malformed variants reject. Cross-flow Triggerer/ToFlowID
preserved.
- server server_test.go — full HTTP surface, including SSE replay+tail
with a real httptest.Server.
- adapter mapper_test.go — every HelmRelease.status.conditions[Ready]
transition + multi-dependsOn fan-out + family-label/heuristic + region
fallback.
Verification done locally:
- (cd products/openova-flow/server && go build ./... && go test ./...) — PASS
- (cd products/openova-flow/adapter-flux && go build ./... && go test ./...) — PASS
- helm template platform/openova-flow-server/chart/ — renders cleanly
- helm template platform/openova-flow-emitter/chart/ — renders cleanly
- bash scripts/check-bootstrap-deps.sh — PASS (drift=0)
Agent #3 follow-ups (called out in slot 57's HelmRelease comments):
- Thread SOVEREIGN_DEPLOYMENT_ID + REGION_KEY into the
postBuild.substitute env in infra/hetzner/cloudinit-control-plane.tftpl
so the emitter's flowId/regionKey become per-deployment + per-region
automatically. Today the slot uses SOVEREIGN_FQDN as the flowId
fallback and "primary" as the regionKey default; per-Sovereign overlays
can override pre-Agent-#3.
- catalyst-api proxy at /sovereign/api/v1/flows/{id}/stream so the
Sovereign Console canvas hits a single in-tree origin.
Co-authored-by: e3mrah <1234567+e3mrah@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>