Files
ihp-railiance-probe/DeploymentBlueprint.md
tegwick b818866c7f docs: document Gitea registry token workaround
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>
2026-05-02 17:09:48 +02:00

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` |