2026-05-02 16:19:46 +02:00
# Deployment Blueprint — ihp-railiance-probe
Textual equivalent of a C4 Deployment Diagram covering the full
build-to-production cycle. Read top-to-bottom as the artifact flows through
each node.
---
## Deployment Nodes
### Node: Developer Workstation
- **Host**: `localhost` (WSL2 on Windows, `linux/x86_64` )
- **Role**: source editing, flake authoring, orchestration
- **Key tools**: git, sshpass, skopeo (client), nix (local, for dry-runs only)
- **Constraints**: not used for Haskell compilation — insufficient RAM for GHC
on large module graphs
### Node: haskelseed (Build Server)
- **Host**: `192.168.178.135` (Alpine Linux VM, LAN-local to workstation)
- **SSH access**: `root` / `hcs26!x` (password) or via `ssh haskelseed` alias
- **Role**: sole Haskell compilation and `nix build` host
- **Resources**: 2 CPU, ~3.8 GiB RAM, 100 GB NVMe at `/nix` (`/dev/sdb1` )
- **Nix store**: `/nix/store` on dedicated volume; store paths survive reboots
- **Key constraint**: `libHSghc-9.10.3-5702.a` in the Nix store may be truncated
(287 MB vs full 289 MB); must be patched before production builds if affected
(see `inter-hub/HaskellVibePrimer.md` §Bug 2)
- **GHC version**: 9.10.3 (from IHP v1.5 flake)
- **GHCRTS**: `-A32m -M2g` (heap ceiling to prevent OOM)
### Node: CoulombCore VPS (Registry + Gitea)
- **Host**: `92.205.130.254`
- **SSH access**: `tegwick` via `id_ops` key (alias `coulombcore` )
- **Role**: Gitea source hosting + built-in OCI container registry
- **Gitea SSH**: port `30022` , alias `gitea-remote` in `~/.ssh/config`
- **Registry endpoint**: `92.205.130.254:32166` (HTTP, no TLS — internal use)
- **Image namespace**: `coulomb/ihp-railiance-probe`
- **Registry auth**: Gitea credentials (same user as Gitea login)
### Node: Railiance01 (Production Cluster)
- **Host**: `92.205.62.239`
- **SSH access**: `tegwick` via `id_custodian_agent` key (alias `railiance01` )
- **Role**: k3s Kubernetes cluster, deployment target
- **Namespace**: `coulomb` (shared with inter-hub)
- **Image pull**: cluster pulls from `92.205.130.254:32166` (LAN-adjacent VPS,
no auth needed if registry is public or cluster has credentials configured)
- **Ingress**: Traefik (k3s default); routes via IngressRoute or Ingress manifest
---
## Deployment Pipeline — Step by Step
```
[Workstation]
│
│ git push (SSH via gitea-remote)
▼
[CoulombCore — Gitea]
│
│ (no CI yet; developer triggers manually)
│
▼
[Workstation]
│
│ scp flake.nix + source → haskelseed
│ (or: git push + git pull on haskelseed)
▼
[haskelseed — Build]
│ nix build .#docker
│ → evaluates flake.nix
│ → builds inter-hub-models (GHC, 477 modules) ← cached after first build
│ → builds inter-hub-lib (GHC, 199 modules)
│ → builds inter-hub-binaries
│ → assembles OCI tarball (result → /root/ihp-railiance-probe/result)
│
2026-05-02 17:09:48 +02:00
│ # Gitea's registry token realm points to port 80; skopeo must use a
│ # pre-fetched token to avoid following that misconfigured URL.
│ TOKEN=$(curl -s \
│ "http://92.205.130.254:32166/v2/token?service=container_registry\
│& scope=repository:coulomb/< APP > :push,pull" \
│ -u 'tegwick:< GITEA_API_KEY > ' | awk -F'"' '/token/{print $4}')
│ skopeo copy --insecure-policy --dest-tls-verify=false \
│ --dest-registry-token "$TOKEN" \
│ docker-archive:result \
│ docker://92.205.130.254:32166/coulomb/< APP > :< SHA >
2026-05-02 16:19:46 +02:00
▼
[CoulombCore — Registry]
│ image stored as coulomb/ihp-railiance-probe:< SHA >
│
▼
[Railiance01 — Kubernetes / k3s]
│ helm upgrade --install ihp-railiance-probe ./chart
│ --set image.tag=< SHA >
│ --namespace coulomb
│
│ k3s pulls image from 92.205.130.254:32166
│ Deployment → ReplicaSet → Pod (RunProdServer binary)
│ Service (ClusterIP) → IngressRoute (Traefik)
▼
[External / Browser]
GET https://probe.railiance.example/
```
---
## Container: ihp-railiance-probe Application
- **Base**: IHP unoptimized Docker image (`config.packages.unoptimized-docker-image` )
- **Entry point**: `/bin/RunProdServer`
- **Exposed port**: `8000` (IHP default)
- **Environment variables** (injected via Kubernetes Secret / ConfigMap):
| Variable | Purpose |
|----------|---------|
| `IHP_SESSION_SECRET` | Session encryption key (32+ random bytes, base64) |
| `DATABASE_URL` | PostgreSQL connection string |
| `IHP_BASEURL` | External URL shown in links (e.g. `https://probe.coulomb.example` ) |
- **PostgreSQL**: deployed as a separate pod (`bitnami/postgresql` ) or uses the
shared CoulombCore postgres — TBD per cluster capacity
---
## Artifact Versioning
| Artifact | Identifier | Retention |
|----------|------------|-----------|
| Git commit | `<SHA>` (short, from `git rev-parse --short HEAD` ) | permanent in Gitea |
| OCI image | `coulomb/ihp-railiance-probe:<SHA>` | keep last 5 tags |
| Helm release | `ihp-railiance-probe` in namespace `coulomb` | single release, upgraded in-place |
| Nix build result | `/root/ihp-railiance-probe/result` symlink on haskelseed | GC'd by `nix store gc` |
---
## Known Infrastructure Constraints
| Constraint | Impact | Mitigation |
|------------|--------|------------|
| haskelseed 2 CPU / 3.8 GB RAM | Full GHC build saturates RAM | `GHCRTS=-A32m -M2g` ; `-j1` in flake |
| GHC 9.10.3 `.hi` overflow (>274 MB) | Crash after all modules compile | ActualTypes postUnpack overlay in flake.nix |
| GHC 9.10.3 `libHSghc.a` truncated | Crash at position 287,686,318 | Direct archive patch on haskelseed (one-time; check after flake lock update) |
| Registry on HTTP (no TLS) | k3s defaults to HTTPS for pulls | Configure k3s `registries.yaml` with mirror entry for `92.205.130.254:32166` |
2026-05-02 17:09:48 +02:00
| Gitea registry token realm misconfigured | `Bearer realm` points to `gitea.coulomb.social:80` but Gitea is on port 32166; skopeo follows the realm URL and gets 404 | Pre-fetch token via `curl` against port 32166 and pass with `--dest-registry-token` ; do NOT rely on skopeo's automatic token fetch |
2026-05-02 16:19:46 +02:00
| No CI runner yet | Manual build + push | Phase 6 of workplan adds Gitea Actions runner on haskelseed |
---
## Key File Locations
| File | Node | Path |
|------|------|------|
| Nix flake | Workstation + haskelseed | `flake.nix` |
| Helm chart | Workstation | `chart/` |
| GHC archive (may be truncated) | haskelseed | `/nix/store/ffg3yf2ypnbz3hc31y7nglrkihz0if01-ghc-9.10.3/lib/ghc-9.10.3/lib/x86_64-linux-ghc-9.10.3/ghc-9.10.3-5702/libHSghc-9.10.3-5702.a` |
| Build log | haskelseed | `/tmp/build<N>.log` |
| k3s registries config | Railiance01 | `/etc/rancher/k3s/registries.yaml` |