Commit Graph

58 Commits

Author SHA1 Message Date
jgrusewski
4afaa5e248 fix(infra): resolve double-declared NAT gateway (kapsule vs public-gateway)
The real gateway foxhunt-gw (42653780) was an unmanaged orphan: kapsule state never had a gateway,
public-gateway state referenced a deleted gateway (7a7ffaa5) -> BOTH modules' terragrunt apply would
have created a duplicate gateway (risking cluster egress). Fix:
- kapsule module: removed the legacy gateway/dhcp/gateway_network resources (pre-refactor duplicate,
  never in kapsule state; cluster only needs the private_network which stays). kapsule plan now clean.
- public-gateway module (canonical, IPAM mode): state rm the stale 7a7ffaa5 refs + imported the real
  gateway/gateway_network/IP (42653780 / 661c8543 / dc419a10); set bastion_enabled=true to match the
  live gw. public-gateway plan now 'No changes'. Live gateway untouched throughout.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 14:05:50 +02:00
jgrusewski
a252119fd4 infra(kapsule): remove h100x2 + h100-sxm pools
Reasons:
- h100-sxm: Scaleway account quota is 0/0 for the SXM instance type
  (cp_servers_type_H100_SXM_2_80G). The pool was perpetually trying
  to maintain size=1 with a creation_error node, which JAMMED the
  cluster autoscaler. That blocked L40S scale-up entirely during
  today's alpha-perception cluster run.
- h100x2: more expensive than current workloads justify. Every
  training path (alpha perception, future PPO) fits on either the
  single-GPU H100 or L40S.

Removed:
- Two `scaleway_k8s_pool` resources from infra/modules/kapsule/main.tf
- Six variables (enable + type + max_size for each pool)
- Two outputs (pool_id for each)
- Corresponding inputs in infra/live/production/kapsule/terragrunt.hcl

The live cluster has the SXM pool stuck node manually deleted via
`scw k8s node delete` (this commit-session); the pool resource itself
will be destroyed on next `terragrunt apply`.

Post-cleanup pool inventory:
- platform (DEV1-L × 3)
- ci-training-h100  (H100-1-80G, max 1)  <- regular single-GPU
- ci-training-l40s  (L40S-1-48G, max 1)  <- primary training target
- ci-compile-cpu    (POP2-HC, max 4)
- ci-compile-cpu-hm (POP2-HM, max 1)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 23:43:12 +02:00
jgrusewski
86de265fc8 infra(kapsule): add ci-compile-cpu-hm pool for 9-quarter fxcache
Two 9-quarter precompute_features runs OOM-killed on the existing
ci-compile-cpu pool (POP2-HC-32C-64G, 56Gi cgroup limit):
  - train-wq8b8: exit 137 ~12s after "OFI computed" at ~57Gi
  - train-2l6p4: exit 137 ~12s after "OFI computed" at ~52Gi peak
    (despite the into_iter + drop(feature_vectors) +
    normalize_in_place refactors landed in a27cb40a9 + 623ebfcf7,
    which trimmed ~12GB of avoidable retention)

The remaining ~52-60GB peak is the irreducible working set at the
post-OFI / pre-alpha-pipeline step:
  - 9-quarter MBP-10 snapshots (~10GB)
  - 199M front-month trades as Mbp10Trade (~13GB)
  - 17.8M bars × features + targets + OFI buffers (~14GB)
  - alpha_snapshots (move-handoff from all_snapshots, ~10GB)
  - feature/target Vecs (~7GB) + Rust allocator overhead

Adds a dedicated high-memory pool sized for this rare path:

- New scaleway_k8s_pool.ci_compile_cpu_hm (POP2-HM-32C-256G,
  32 vCPU + 256GB RAM) with size=0 + min_size=0 autoscaling. Costs
  zero when idle; autoscaler provisions one node when a pod targets
  `nodeSelector: ci-compile-cpu-hm`.
- New variables: enable_ci_compile_cpu_hm_pool,
  ci_compile_cpu_hm_type (default POP2-HM-32C-256G),
  ci_compile_cpu_hm_max_size (default 1).
- terragrunt.hcl: enable the pool, max_size=1.
- train-template.yaml: ensure-fxcache nodeSelector pinned to
  ci-compile-cpu-hm; memory limit raised 56Gi → 200Gi. The
  ci-compile-cpu pool stays as the standard CI compile target for
  ensure-binary + every other CPU-heavy task.

Apply with:
  cd infra/live/production/kapsule
  terragrunt apply -target=module.kapsule.scaleway_k8s_pool.ci_compile_cpu_hm
  kubectl apply -n foxhunt -f ../../../../infra/k8s/argo/train-template.yaml

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 11:13:03 +02:00
jgrusewski
70fca46227 revert: H100 pool back to min_size=0 — too expensive to keep warm
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 09:13:46 +02:00
jgrusewski
33e9d80600 infra: H100 pool min_size=1 — keep node warm, skip 15-min driver install
Scaleway Kapsule GPU nodes need NVIDIA driver DKMS compile on cold
start (~15 min). Setting min_size=1 keeps the node warm between runs.
Scale to 0 manually when done training for the day.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 09:12:36 +02:00
jgrusewski
ddfa57af11 infra: VPC gateway in Terraform + DNS deadlock prevention
- Added scaleway_vpc_public_gateway + DHCP + gateway_network to TF
  (was manually created, now codified with push_default_route=true)
- Added scripts/safe-node-replace.sh — one-at-a-time with DNS verification
- Added dns-bootstrap-policy.yaml — incident documentation + recovery procedure
- Bastion enabled (port 61000) for emergency SSH access

Prevention: NEVER replace all nodes at once. Use safe-node-replace.sh.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 00:38:20 +01:00
jgrusewski
a0bd744e5b fix(infra): remove stale DNS outputs, add chat/mail FQDNs
Remove prometheus_fqdn and monitor_fqdn outputs that referenced
deleted DNS records. Add chat_fqdn and mail_fqdn for Mattermost
and Stalwart services.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 14:47:04 +01:00
jgrusewski
9e8b9422cf feat(infra): deploy Mattermost on platform nodes with CI webhook egress
Add Mattermost Team Edition deployment (PVC, Service, NetworkPolicy),
DNS records (chat.fxhnt.ai, mail.fxhnt.ai), Tailscale nginx proxy
server block with WebSocket upgrade support, and Mattermost egress
rules to all 5 Argo workflow NetworkPolicies for exit handler webhooks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 09:20:32 +01:00
jgrusewski
fb53b81a93 infra: automate terragrunt via Argo CI, clean up kapsule module, harden PAT rotation
- Add terragrunt-apply step to Argo CI pipeline (plan+apply on main push
  when infra/live/ or infra/modules/ change)
- Bake OpenTofu 1.9.0 + Terragrunt 0.77.12 into ci-builder-cpu image
  with SHA256 checksum verification
- Remove 3 ghost node pools (foxhunt, gitlab, h100-sxm8) from kapsule
  module to match Scaleway reality
- Make terragrunt.hcl single source of truth (remove variable defaults)
- Fix GitLab TF state lock methods (POST/DELETE for HTTP backend)
- Harden PAT rotation: more retries, verification step, recovery docs
- Add weekly PAT expiry check CronJob (warns 14 days before expiry)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 23:25:33 +01:00
jgrusewski
53eb82c193 fix(ci): use partial clone for robust SHA resolution + upgrade compile node to POP2-HC
- Replace fragile `git fetch --depth=1 origin "$SHA"` (fails with short SHAs)
  with `git clone --no-checkout --filter=blob:none` + `git checkout "$SHA"`
  across all 3 Argo templates (5 clone blocks total)
- Upgrade CI compile pool from POP2-32C-128G to POP2-HC-32C-64G
  (higher clock EPYC cores, 28% cheaper at €0.851/hr vs €1.18/hr)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 22:16:08 +01:00
jgrusewski
68b6aa8313 feat(infra): migrate container registry from SCW to internal GitLab
Full migration off Scaleway Container Registry to internal GitLab
registry backed by MinIO S3. All 4 images (ci-builder, ci-builder-cpu,
foxhunt-runtime, foxhunt-training-runtime) rebuilt in internal registry.

Registry & images:
- All image refs → gitlab-registry.foxhunt.svc.cluster.local:5000/root/foxhunt/
- imagePullSecrets: scw-registry → gitlab-registry
- Kaniko build template: two-step DAG (git-clone → kaniko-build) with shared PVC
- Kaniko layer cache enabled at root/foxhunt/cache
- AWS_ACCESS_KEY_ID: $SCW_ACCESS_KEY → $MINIO_ACCESS_KEY in .gitlab-ci.yml

Network policies:
- ci-pipeline: add HTTP/80, registry/5000, webservice/8181 egress rules

DNS & Tailscale proxy cleanup:
- Remove ci, prometheus, monitor DNS records (no longer exposed)
- Rename s3 → minio DNS record
- Remove Argo UI, Prometheus, monitor nginx server blocks
- Remove argo-htpasswd volume mount
- Tailscale proxy nodeSelector: infra → platform

Terraform cleanup:
- Delete infra/modules/registry/ (SCW CR namespace)
- Delete infra/modules/object-storage/ (SCW S3 buckets)
- Delete infra/modules/secrets/ (SCW secrets)
- Delete corresponding live configs
- TF state backend: S3 → GitLab HTTP

Argo workflows:
- Add events/ (GitLab push eventsource + ci-pipeline sensor)
- ci-pipeline + training templates: SCW → internal registry
- Delete obsolete compile-training-template.yaml

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 13:52:24 +01:00
jgrusewski
e9d6c7825d feat(infra): sync Terraform GPU pools with actual SCW state
Rename ci-training → ci-training-l40s, add H100/H100x2/H100-SXM/H100-SXM-8
pool resources (all autoscaling 0→1). Remove dev pool (DevPod on platform).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 11:37:31 +01:00
jgrusewski
370752f253 feat(infra): expose Argo Workflows UI at ci.fxhnt.ai
Rename DNS record from argo→ci and update nginx proxy server_name
to match. Applied via terragrunt and kubectl.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 22:58:40 +01:00
jgrusewski
a2468846e1 feat(argo): add Argo Workflows infrastructure for training orchestration
- Helm values (controller + server on platform node, MinIO artifact repo)
- WorkflowTemplate: parameterized 5-step DAG (fetch→hyperopt→train→eval→upload)
- Nginx proxy for argo.fxhnt.ai → Argo Server :2746
- DNS A record for argo.fxhnt.ai
- MinIO bucket foxhunt-training-results for Argo artifacts
- Kustomization for kubectl apply -k

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 22:58:40 +01:00
jgrusewski
cc209aec7e feat(infra): replace PVC binary distribution with MinIO S3 fetch
Eliminate foxhunt-binaries and training-binaries PVCs — services and
training jobs now fetch binaries from MinIO via rclone initContainers.
This removes L40S GPU autoscale-for-PVC-writes, removes RWO node
affinity constraints, and requires zero image rebuilds.

Changes:
- .gitlab-ci.yml: replace ~115 lines of binary-writer pod logic with
  aws s3 cp to MinIO (~25 lines)
- 8 service YAMLs + 2 GPU overlays: add rclone initContainer + emptyDir
- Training job template: MinIO rclone fetch replaces PVC copy
- Delete foxhunt-binaries-pvc.yaml and training-binaries-pvc.yaml
- Add s3.fxhnt.ai DNS record (Terraform) and nginx proxy block
- Replace pod-writer-deploy skill with MinIO-based deploy skill

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 16:46:57 +01:00
jgrusewski
6b67f6193a infra: expose monitoring service via monitor.fxhnt.ai Tailscale proxy
Add DNS record, nginx gRPC proxy block, network policy for Tailscale
ingress, and auto-detect fxhnt.ai in fxt monitor URL derivation.
Show GPU telemetry even when no training sessions are active.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 22:56:40 +01:00
jgrusewski
d501d53c9d chore(infra): remove dead Cockpit TF, CI-CD dashboards, stale Grafana configs
- Delete infra/modules/cockpit/ and infra/live/production/cockpit/
  (Scaleway Cockpit replaced by self-hosted Grafana+Prometheus+Loki+Tempo)
- Delete CI-CD dashboards (foxhunt-ci-pipelines, gitlab-services) and
  grafana-dashboards-cicd ConfigMap from K8s
- Delete config/grafana/ — unreferenced old dashboards (15 files)
- Delete config/monitoring/grafana/ — unreferenced DQN staging dashboard
- Delete crates/ml/grafana/ — unreferenced ML performance dashboard
- Delete services/broker_gateway_service/grafana/ — unreferenced
- Delete .claude/agents/devops/ci-cd/ — GitHub Actions agent (we use GitLab CI)
- Fix grafana-values.yaml: dashboard folder GitLab → Foxhunt,
  hardcoded adminPassword → K8s secret (grafana-admin),
  gitlab-overview → node-exporter (correct name for gnetId 1860)
- Remove CI-CD group from import.sh ConfigMap groups and API fallback

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 15:13:40 +01:00
jgrusewski
6e03c01e2c chore(infra): remove dead TF code — ci-rl pool and binaries S3 bucket
ci-rl pool was permanently disabled (enable_ci_rl_pool=false), all
training consolidated on ci-training (L40S). foxhunt-binaries S3
bucket was never created — binaries now deploy via shared PVC.

Also: deleted orphaned infra/live/production/ci-runner/ directory,
cleaned up stale Grafana ReplicaSets, recreated missing grafana PVC,
deleted orphaned Released PV, synced runner Helm configmap.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 12:22:59 +01:00
jgrusewski
58f8478320 infra(tf): add platform pool, rename services → foxhunt in Kapsule module
Terraform changes for 3-pool node split:
- New `platform` pool resource (DEV1-L, databases + monitoring)
- Renamed `services` → `foxhunt` pool (DEV1-L, max_size 1)
- Updated variables, outputs, and live terragrunt inputs
- Updated implementation plan with Terraform-based workflow

Apply in 2 phases:
  Phase 1: terragrunt apply -target=scaleway_k8s_pool.platform
  Phase 2: terragrunt apply (after databases migrated to platform)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 09:59:21 +01:00
jgrusewski
c97c02677e fix(infra): rename L4 pool ci-compile → ci-rl, remove stale moved blocks
Applied terragrunt to recreate the L4 GPU pool with the correct name
`ci-rl` (was `ci-compile` due to immutable Scaleway pool names).
Updated all K8s manifests and comments to match. Removed the 3 stale
`moved` blocks from main.tf since the state renames are now applied.

Pool naming is now consistent across Terraform, Scaleway, and K8s configs:
- ci-compile-cpu (POP2-32C-128G) — CPU compilation
- ci-rl (L4-1-24G) — RL training / CUDA compile
- ci-training (L40S-1-48G) — supervised training + hyperopt

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 13:59:44 +01:00
jgrusewski
48316d6874 feat(infra): add api.fxhnt.ai DNS + gRPC reverse proxy for API Gateway
Add nginx server block for api.fxhnt.ai with grpc_pass to the
api-gateway ClusterIP service. Add Scaleway DNS A record via Terraform
module. Fix fxt gRPC client to enable TLS when connecting to https://
endpoints (tonic requires explicit ClientTlsConfig).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 22:41:55 +01:00
jgrusewski
3bdefe86d5 feat(monitoring): production dashboards, remove Cockpit, add Kapsule logs
- Remove Cockpit remote_write from GitLab Prometheus (all metrics local)
- Uninstall k8s-monitoring Helm (Alloy no longer pushes to Cockpit)
- Deploy standalone node-exporter DaemonSet + kube-state-metrics
- Add 10 extra Prometheus scrape configs: node-exporter, kube-state-metrics,
  kubelet, cadvisor, DCGM GPU, foxhunt services, Loki, Tempo, Promtail,
  prometheus.io annotated pods
- Provision 9 Grafana dashboards via sidecar (ConfigMap-based):
  overview, services, GPU/training, infrastructure, logs, CI/CD pipelines,
  cluster overview, GPU overview, GitLab services
- Expand Promtail to ingest kube-system + GitLab runner + node-level logs
- Add dashboard.fxhnt.ai DNS record + nginx routing to Grafana
- Update CI deploy job to apply new monitoring manifests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 22:20:32 +01:00
jgrusewski
5287bbbf66 infra: add foxhunt-binaries S3 bucket for binary distribution
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 17:56:57 +01:00
jgrusewski
c731bef759 feat(infra): split training image into RL + supervised, rename ci-compile → ci-rl
RL models (DQN/PPO) only need basic CUDA runtime (~2GB), while supervised
models need cuDNN (~4GB). Splitting saves ~2GB pull time per RL job.
Rename the L4 GPU pool from ci-compile to ci-rl to reflect its actual use.
Add DaemonSet image pre-puller to cache training images on GPU nodes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 10:16:42 +01:00
jgrusewski
e67f828f3c fix(infra): use Kapsule built-in pool-name label for node selection
Kapsule doesn't allow node-labels in kubelet_args. Instead, use the
auto-assigned k8s.scaleway.com/pool-name label that every node gets.

- Revert kubelet_args from Terraform (not supported)
- Switch runner node_selector: pool→k8s.scaleway.com/pool-name
- Update CI pipeline KUBERNETES_NODE_SELECTOR to match
- Helm upgraded both runners (CPU + GPU)

No more manual kubectl label after node scale-up.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 23:37:43 +01:00
jgrusewski
a9f70b5145 fix(infra): add kubelet node-labels to all Kapsule pools
Nodes from autoscaler now get pool=<name> labels automatically via
kubelet_args, eliminating the need to manually label nodes after
scale-up events.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 23:30:14 +01:00
jgrusewski
f336ed01b4 fix(infra): update CPU compile pool to POP2-32C-128G (GP1-L has no quota)
GP1-L and PRO2-L both have 0/0 quota in fr-par-2. POP2-HC-32C-64G had
insufficient root disk (20GB). POP2-32C-128G with 100GB SBS root works.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 21:36:13 +01:00
jgrusewski
a5d9cec2cd feat(infra): add POP2-HC-32C-64G CPU compile pool for 4x faster builds
Split runners into CPU (compile/build/deploy) and GPU (training):
- CPU runner: tags kapsule,rust,docker → ci-compile-cpu (32 vCPU, 64GB)
- GPU runner: tags kapsule,gpu → ci-compile (L4) or ci-training (L40S)

Changes:
- Add ci-compile-cpu Terraform pool (POP2-HC-32C-64G, autoscale 0-1)
- Set CARGO_BUILD_JOBS=30 to use all CPUs for Rust compilation
- Increase CPU request to 28000m/31000m (was 7000m/7800m on L4)
- Add docker tag to deploy/infra jobs for explicit CPU runner routing
- Update CI header with new pool routing documentation

Cost: EUR 0.85/h (vs EUR 1.1/h for L4) — cheaper AND 4x faster compile

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 21:08:42 +01:00
jgrusewski
1a41f9475a refactor(infra): update all manifests for pool rename (always-on→services, ci-build→ci-compile/ci-training)
All K8s nodeSelectors, CI config, runner config, monitoring docs, and
smoke tests now reference the new pool names provisioned by terragrunt.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 10:33:34 +01:00
jgrusewski
208f6f2392 refactor(infra): rename pools — always-on→services, ci-build→ci-training
Rename Terraform resources and Scaleway pool names for clarity:
- always_on → services (runs postgres, redis, microservices)
- ci_build → ci_training (L40S for hyperopt + training jobs)

Includes:
- moved{} blocks to prevent Terraform state destroy+recreate
- Output renames + new outputs for ci-compile and ci-training pools
- node_selector_overwrite_allowed in runner config (enables per-job
  KUBERNETES_NODE_SELECTOR_* overrides for L4/L40S routing)

MIGRATION (after terragrunt apply):
1. terragrunt apply — recreates pools with new names
2. Update K8s manifests: always-on→services in nodeSelectors
   (postgres, redis, questdb, tailscale, idle-reaper, postgres-init)
3. Update .gitlab-ci.yml: ci-build→ci-training in training jobs
4. Update runner-values.yaml: default pool ci-build→ci-compile
5. helm upgrade gitlab-runner + kubectl apply changed manifests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 10:20:41 +01:00
jgrusewski
ce65e4ca42 feat(infra): add L4 compile pool alongside L40S training pool
Split GPU workloads across two pools:
- ci-compile (L4-1-24G): for cargo check/test/build — cheaper, same
  CUDA CC 8.9 as L40S, sufficient 24GB VRAM for compilation
- ci-build (L40S-1-48G): for hyperopt + training — 48GB VRAM needed
  for large model training batches

Runner defaults to ci-compile; training jobs override to ci-build
via KUBERNETES_NODE_SELECTOR. Both pools autoscale to zero when idle.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 10:11:54 +01:00
jgrusewski
fbbd6bac22 feat(infra): enable always-on pool autoscaling to 2 nodes
Services moved off L40S GPU node to always-on DEV1-M pool.
Single DEV1-M couldn't fit all services, so enable autoscaling
with max_size=2 to allow a second node when needed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 10:03:09 +01:00
jgrusewski
1de1769c4f refactor(infra): remove H100/L4 pools, use kapsule dependency for gateway
- Remove gpu-training (H100) and gpu-inference (L4) pool resources,
  variables, and outputs from kapsule module — L40S handles all GPU work
- Switch public-gateway from hardcoded private_network_id to
  dependency.kapsule.outputs.private_network_id

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 01:32:05 +01:00
jgrusewski
0c5da1fb55 fix(infra): fix cockpit module for SCW provider v2.69.0
- Add required retention_days to cockpit_source (31 = free tier)
- Remove deprecated scaleway_cockpit data source (endpoints null)
- Use grafana_user resource for grafana_url output instead

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 00:45:36 +01:00
jgrusewski
51257dbe13 feat(infra): add Cockpit metrics + NAT gateway
Workstream 1 — NAT Gateway:
- New public-gateway TF module (VPC-GW-S, masquerade, push_default_route)
- Disable public IPs on all 6 Kapsule node pools
- Terragrunt live config with kapsule dependency

Workstream 2 — Scaleway Cockpit Metrics:
- New cockpit TF module (data source, push token, Grafana user)
- Grafana Alloy k8s-monitoring Helm values (node-exporter, kube-state-metrics)
- DCGM Exporter DaemonSet for GPU metrics (affinity on nvidia.com/gpu.present)
- GitLab Prometheus remote_write to Cockpit (filtered to gitlab_/gitaly_/sidekiq_)
- Apply-order runbook (3 phases: Cockpit → GitLab → NAT)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 00:33:55 +01:00
jgrusewski
f13b0df6e8 feat(infra): upgrade CI to L40S + mold linker for faster builds
- Switch linker from lld to mold (~2-5x faster linking for large binaries)
  - Install mold 2.35.1 in CI builder Dockerfile
  - Update .cargo/config.toml: -fuse-ld=mold
- Upgrade CI build pool: L4-1-24G → L40S-1-48G (~2x training throughput)
  - Increase max_size from 1 to 2 (allows concurrent jobs, fixes scheduling deadlocks)
  - Update runner resource limits for L40S node (24 vCPU, 96GB)
- Update runner-values.yaml comments and .gitlab-ci.yml header

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 00:05:23 +01:00
jgrusewski
7b9da53603 infra(ci): switch CI builds from H100 to L4 — 4.7x cost reduction
Create dedicated ci-build node pool (L4-1-24G, €0.75/hr) replacing
H100 (€3.50+/hr) for CI builds. H100 reserved for training only.

- Add ci-build pool to Terraform (L4-1-24G, scale-to-zero, fr-par-2)
- Update GitLab Runner to target ci-build pool with L4-sized limits
- Change CUDA_COMPUTE_CAP from 90 (H100) to 89 (L4) in CI
- Dockerfile.training keeps CUDA_COMPUTE_CAP=90 for H100 training

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 23:30:04 +01:00
jgrusewski
d271315e7e infra(dns): add grafana + prometheus A records to Terraform
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 21:46:56 +01:00
jgrusewski
b321fa47a7 infra(object-storage): add sccache bucket to Terraform
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 21:46:45 +01:00
jgrusewski
c80899066c infra(registry): add foxhunt-ci namespace to Terraform
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 21:46:39 +01:00
jgrusewski
6b9e58feee fix(infra): add wait_for_pool_ready=false for gpu-dev pool
Scale-to-zero pools start with 0 nodes, so Terraform's default
wait_for_pool_ready=true always fails with "state warning, wants ready".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 21:28:37 +01:00
jgrusewski
8cdbdf9625 infra: add gpu-dev pool for DevPod remote development
GP1-L (16 vCPU, 64GB), autoscaling 0→1, dedicated for dev pods.
Separate from CI (H100) and inference (L4) pools.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 21:12:49 +01:00
jgrusewski
10d81b0703 infra(kapsule): consolidate CI builds onto H100 gpu-training pool
Remove orphaned `ci` pool (GP1-XS, 4 vCPU wasted — nothing scheduled to it).
Remove `ci-build` pool (GP1-M) — builds now run on gpu-training (H100-1-80G:
24 vCPU, 240GB, real CUDA). This eliminates the need for CUDA stubs,
separate test-gpu jobs, and ml crate exclusions.

Pool layout after:
  always-on    DEV1-M        (core services, always on)
  gitlab       GP1-XS        (GitLab CE + runner manager, always on)
  gpu-training H100-1-80G    (CI builds + ML training, scale-to-zero)
  gpu-inference L4-1-24G     (trading inference, scale-to-zero)

Build pod limits bumped to 16 vCPU / 64GB (from 6/12GB) to use H100 capacity.
Runner now has `gpu` tag — all tests including ml crate run in single job.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 17:20:26 +01:00
jgrusewski
5886ae57a6 chore: remove Gitea CI config and old ci-runner module
Gitea replaced by GitLab CE on Kapsule. CI runner replaced by
GitLab Runner with Kubernetes executor on ci-build node pool.

Removed:
- .gitea/workflows/ci.yaml (Gitea Actions config)
- infra/modules/ci-runner/ (Scaleway VM-based runner)
- infra/live/production/ci-runner/ (Terragrunt live config)

Note: The ci-runner VM instance still needs manual `terragrunt destroy`
before the Scaleway resource can be released.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 01:14:59 +01:00
jgrusewski
840fe15471 Merge branch 'worktree-gitlab-migration'
GitLab CE migration infrastructure: Helm values, Tailscale proxy,
CI/CD pipeline, GitLab Runner, DNS, K8s manifests, S3 storage.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 00:56:29 +01:00
jgrusewski
184c07bb82 Merge branch 'worktree-audit-fixes'
# Conflicts:
#	infra/modules/secrets/main.tf
#	infra/modules/secrets/variables.tf
2026-02-25 00:33:10 +01:00
jgrusewski
86f7f1fa76 fix: comprehensive audit — real brokers, deployment fixes, production safety
Codebase audit identified 23 findings across 4 dimensions (production safety,
code health, deployment readiness, test quality). This commit fixes all of them.

Broker execution layer (was entirely stubbed):
- Real IBKR TWS client via ibapi crate (950+ lines, feature-gated)
- ICMarkets ctrader-openapi now always-on (removed feature flag)
- Real broker routing with health monitoring and exponential backoff reconnect
- Validated against live IB Gateway Docker (6/6 connectivity tests pass)

Deployment blockers:
- Fixed 6 broken Dockerfiles (removed COPY foxhunt-deploy)
- Created foxhunt K8s namespace, secret templates, migration job
- Added liveness probes to all 7 K8s services
- IB Gateway manifest (ghcr.io/gnzsnz/ib-gateway:stable)
- IBKR credentials in Scaleway Secret Manager via Terragrunt
- Fixed port collisions and mismatches across services

Production safety (9 critical + 6 high/medium fixes):
- Asset-class-specific VaR volatility (not flat 2%)
- Real parametric VaR with z-score 95th percentile
- Kyle's lambda regression (100-bar rolling window)
- Per-feature running statistics from historical data
- VWAP-based slippage reference, regime duration tracking
- Real Databento JSON parsing for OHLCV/Trade/Quote

Code health:
- Removed #![allow(dead_code)] from ml, data, config
- Fixed log:: → tracing:: in 4 production files
- Removed dead workspace deps (ratatui, crossterm)

Verified: cargo check --workspace (0 errors), trading_engine 330 tests pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 00:32:10 +01:00
jgrusewski
020e85af2b infra: add DNS module + rename gitlab.fxhnt.ai → git.fxhnt.ai
Keep existing git.fxhnt.ai hostname for GitLab CE (repoint DNS
to new Tailscale IP after deploy). Add Terraform DNS module to
manage the A record via Terragrunt instead of manual scw CLI.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 23:26:51 +01:00
jgrusewski
1c36941ded infra(kapsule): add gitlab and ci-build node pools
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 23:21:14 +01:00
jgrusewski
ce8eeba6fb infra(storage): add GitLab registry and artifacts buckets 2026-02-24 23:21:06 +01:00