generated from coulomb/repo-seed
Gitea's registry token realm is misconfigured — it points to gitea.coulomb.social:80 but Gitea only listens on port 32166. iptables is not available on haskelseed (Alpine). Workaround: pre-fetch the bearer token via curl against port 32166 and pass it to skopeo with --dest-registry-token, bypassing the broken token service URL entirely. Validated with inter-hub:11ff61c on 2026-05-02. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
156 lines
6.6 KiB
Markdown
156 lines
6.6 KiB
Markdown
# 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)
|
|
│
|
|
│ # 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>
|
|
▼
|
|
[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` |
|
|
| 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 |
|
|
| 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` |
|