mirror of
https://github.com/allaunthefox/Research-Stack.git
synced 2026-07-31 03:05:21 +00:00
414 lines
14 KiB
Markdown
414 lines
14 KiB
Markdown
# Infrastructure Status — Research Stack
|
|
|
|
> **Last verified:** 2026-05-23
|
|
> **Scope:** All live nodes, services, and storage in the Tailscale mesh.
|
|
|
|
---
|
|
|
|
## Tailscale Mesh
|
|
|
|
All nodes route through Tailscale. No public ports are open on any node except `microvm-racknerd` (ports 80/443 for Caddy).
|
|
|
|
| Node | Tailscale IP | Hostname | Role | OS | SSH |
|
|
|------|-------------|----------|------|-----|-----|
|
|
| **qfox-1** | 100.88.57.96 | QFox | Garage primary, S3 endpoint, GPU compute, build host | Arch Linux 7.0.9-cachyos | local |
|
|
| **nixos-laptop** | 100.102.173.61 | nixos | Authentik SSO, k3s control plane, storage node | NixOS 26.05 (Yarara) | key OK |
|
|
| **361395-1** | 100.110.163.82 | 361395 | Netcup VPS, Garage storage node | Debian 13 | key OK |
|
|
| **microvm-racknerd** | 100.101.247.127 | MicroVM-Racknerd | Caddy reverse proxy, public edge | Debian (microVM) | root password OK |
|
|
| **nixos-steamdeck-1** | 100.85.244.73 | steamdeck | GPU compute, planned edge LLM (3B-7B) | NixOS | just onboarded |
|
|
| **dracocomp** | 100.100.140.27 | — | offline / unreachable | — | unreachable 3+ days |
|
|
|
|
---
|
|
|
|
## Public Edge: Caddy on microvm-racknerd
|
|
|
|
Caddy v2.11.3 with Porkbun DNS plugin handles all public HTTPS traffic.
|
|
|
|
### Wildcard TLS
|
|
|
|
- **Certificate:** `*.researchstack.info` + `researchstack.info`
|
|
- **Issuer:** Let's Encrypt E7
|
|
- **Challenge:** DNS-01 via Porkbun API
|
|
- **Valid:** 2026-05-21 → 2026-08-19
|
|
- **Renewal:** automatic
|
|
|
|
### Domains & Routing
|
|
|
|
| Domain | Caddy Action | Upstream (Tailscale) | Notes |
|
|
|--------|-------------|----------------------|-------|
|
|
| `researchstack.info` | `forward_auth` + `reverse_proxy` | `100.102.173.61:30803` | Homer dashboard (landing page) |
|
|
| `chat.researchstack.info` | `forward_auth` + `file_server` | `100.102.173.61:9000` (auth only) | Placeholder HTML |
|
|
| `dash.researchstack.info` | `forward_auth` + `reverse_proxy` | `100.102.173.61:30802` | Heimdall (k3s NodePort) |
|
|
| `status.researchstack.info` | `forward_auth` + `reverse_proxy` | `100.102.173.61:30801` | Uptime Kuma (k3s NodePort) |
|
|
| `auth.researchstack.info` | `reverse_proxy` (direct) | `100.102.173.61:9000` | Authentik SSO (no forward_auth loop) |
|
|
|
|
### Caddy Service
|
|
|
|
- **Config:** `/etc/caddy/Caddyfile`
|
|
- **Service:** `caddy.service` (systemd)
|
|
- **Status:** active
|
|
- **Env:** Porkbun credentials via systemd drop-in (`/etc/systemd/system/caddy.service.d/override.conf`)
|
|
- **Disk:** 9.1G total, 6.4G free
|
|
|
|
---
|
|
|
|
## Identity: Authentik on nixos-laptop
|
|
|
|
Authentik runs as standalone Podman containers (not k3s). The k3s Helm migration is pending.
|
|
|
|
### Containers
|
|
|
|
| Container | Image | Port | Status |
|
|
|-----------|-------|------|--------|
|
|
| `authentik_postgresql_1` | `postgres:16-alpine` | 5432/tcp | Up, healthy |
|
|
| `authentik_valkey_1` | `valkey/valkey:8-alpine` | 6379/tcp | Up, healthy |
|
|
| `authentik_server_1` | `ghcr.io/goauthentik/server:2026.2.3` | 0.0.0.0:9000→9000, 0.0.0.0:9443→9443 | Up |
|
|
| `authentik_worker_1` | `ghcr.io/goauthentik/server:2026.2.3` | — | Up |
|
|
|
|
### Compose
|
|
|
|
- **Path:** `/home/allaun/authentik/docker-compose.yml`
|
|
- **Network:** `authentik_authentik` (bridge)
|
|
- **Volumes:** `database`, `redis`, `media`, `custom-templates`, `certs`
|
|
|
|
### Valkey Migration
|
|
|
|
Redis was replaced with Valkey 8 (Alpine) on 2026-05-22. The `redis` volume was wiped during migration because Valkey does not read Redis RDB format version 13. Authentik cache/session data was lost but rebuilds automatically on first use.
|
|
|
|
---
|
|
|
|
## k3s on nixos-laptop
|
|
|
|
k3s v1.35.4+k3s1 runs as a single-node control plane. Authentik was **not** successfully migrated to k3s.
|
|
|
|
### Running Services (services namespace)
|
|
|
|
| Pod | Service | NodePort | Status |
|
|
|-----|---------|----------|--------|
|
|
| `uptime-kuma` | `uptime-kuma` | 30801 | Running |
|
|
| `heimdall` | `heimdall` | 30802 | Running |
|
|
| `homer` | `homer` | 30803 | Running |
|
|
| `pulse-receiver` | `pulse-receiver` | 30804 | Running |
|
|
|
|
### Blocked / Broken
|
|
|
|
| Pod | Status | Blocker |
|
|
|-----|--------|---------|
|
|
| `authentik-postgresql-0` | CrashLoopBackOff | Bitnami PostgreSQL subchart: read-only filesystem on `/var/run/postgresql` |
|
|
| `authentik-redis-master-0` | ImagePullBackOff | Bitnami Redis image tags no longer exist |
|
|
| `authentik-server-*` | Running (0/1) | Waiting for PostgreSQL |
|
|
| `authentik-worker-*` | CrashLoopBackOff | Waiting for PostgreSQL |
|
|
| `helm-install-authentik-*` | Error | Helm revision failures cascade |
|
|
|
|
**Decision:** Keep Authentik on standalone Podman. Decommission or fix the k3s Authentik HelmChart when time permits.
|
|
|
|
---
|
|
|
|
## Storage: Garage S3
|
|
|
|
Garage v2.3.0 provides a self-hosted, replicated S3-compatible object store.
|
|
|
|
### Cluster Topology
|
|
|
|
| Node ID | Hostname | Address | Zone | Capacity | Usable | DataAvail |
|
|
|---------|----------|---------|------|----------|--------|-----------|
|
|
| `3e08a71b73fa2b10` | QFox | 100.88.57.96:3901 | local | 780.4 GiB | 68.9 GiB | 1.5 TiB (83.5%) |
|
|
| `75fac43bc53eb201` | 361395 | 100.110.163.82:3901 | fra | 68.9 GiB | 68.9 GiB | 65.2 GiB (52.3%) |
|
|
| `a7e6c283056a4d77` | nixos | 100.102.173.61:3901 | ord | 346.5 GiB | 68.9 GiB | 393.5 GiB (85.8%) |
|
|
|
|
- **Replication factor:** 3
|
|
- **Zone redundancy:** maximum
|
|
- **Effective capacity:** 68.9 GiB (bottlenecked by 361395-1)
|
|
- **Layout version:** 1
|
|
|
|
### Ports
|
|
|
|
| Port | Purpose | Binding |
|
|
|------|---------|---------|
|
|
| 3900 | S3 API | qfox-1 only (localhost + Tailscale) |
|
|
| 3901 | RPC (inter-node) | all nodes, Tailscale |
|
|
| 3903 | Admin API | loopback only |
|
|
|
|
### Buckets
|
|
|
|
| Alias | Purpose |
|
|
|-------|---------|
|
|
| `research-stack` | Primary project objects |
|
|
| `db-scratch` | Active SQLite scratch databases |
|
|
| `rds-overflow` | pg_dump / COPY TO exports |
|
|
| `snap-zone` | ZFS send/receive snapshots |
|
|
| `gdrive-mirror` | Mirror of gdrive:research-stack |
|
|
|
|
### Nodes
|
|
|
|
**qfox-1**
|
|
- Garage runs as systemd service (`garage.service`) under dedicated `garage` user
|
|
- S3 API bound to localhost:3900 (forwarded via SSH tunnel for remote access)
|
|
- NixOS-style persistence not needed (native Arch)
|
|
|
|
**361395-1**
|
|
- Garage runs as systemd service
|
|
- zram enabled: 2G zstd swap
|
|
- Disk: 125G total, 66G free
|
|
|
|
**nixos-laptop**
|
|
- Garage runs as systemd service via NixOS module
|
|
- zram enabled in `/etc/nixos/configuration.nix`
|
|
- Disk: 459G NVMe, 394G free
|
|
- Binary path: not in default `$PATH`; managed by NixOS
|
|
|
|
---
|
|
|
|
## Backups: restic + rclone
|
|
|
|
### Primary restic repo
|
|
|
|
- **Backend:** `s3:http://localhost:3900/research-stack` (Garage)
|
|
- **Password file:** `/etc/garage/restic-password`
|
|
|
|
### Scripts
|
|
|
|
All scripts live in `4-Infrastructure/storage/restic/`:
|
|
|
|
| Script | Purpose |
|
|
|--------|---------|
|
|
| `backup.sh snap [tag]` | Snapshot repo tree → Garage |
|
|
| `backup.sh snap-db [dir]` | Snapshot SQLite scratch DBs |
|
|
| `backup.sh snap-rds <table>` | Stream pg_dump \| zstd → restic stdin |
|
|
| `backup.sh cold-copy` | rclone copy Garage → gdrive:restic-mirror |
|
|
| `backup.sh sync-gdrive` | rclone sync gdrive:research-stack → Garage:gdrive-mirror |
|
|
| `backup.sh forget` | Retention prune (7d/4w/6m) |
|
|
| `backup.sh verify` | restic check --read-data-subset=5% |
|
|
| `backup.sh full` | snap + cold-copy + sync-gdrive + forget |
|
|
|
|
### Schedule
|
|
|
|
- **Daily timer:** `restic-backup.timer` fires at 03:00 ±30 min
|
|
- **Post-commit hook:** `.git/hooks/post-commit` runs `db-consolidate.sh offload` + `consolidate` in background
|
|
|
|
---
|
|
|
|
## Secrets
|
|
|
|
SOPS/age is used for all secrets.
|
|
|
|
- **Age key:** `~/.config/sops/age/keys.txt`
|
|
- **Public key:** `age1tp4vr565zkmvnyulatpyaj6z8zrz7q9mpaypz85yz8rty99crdasualxyr`
|
|
- **Config:** `.sops.yaml` (repo root) + `4-Infrastructure/k3s-flake/.sops.yaml`
|
|
|
|
### Encrypted files (selection)
|
|
|
|
| File | Contents |
|
|
|------|----------|
|
|
| `4-Infrastructure/infra/secrets/credentials.json` | Provider API keys |
|
|
| `4-Infrastructure/infra/secrets/appflowy.env` | AppFlowy secrets |
|
|
| `4-Infrastructure/deploy/cupfox/pre-infect-backup/porkbun.env` | Porkbun API key + secret |
|
|
| `4-Infrastructure/storage/restic/restic.env` | Restic + Garage credentials |
|
|
| `API KEYS/racknerd_510bd9c_root.txt` | Racknerd credentials |
|
|
|
|
### Porkbun DNS
|
|
|
|
- API key + secret stored in SOPS-encrypted `porkbun.env`
|
|
- Used by Caddy for DNS-01 wildcard certificate challenges
|
|
- Verified working: `/ping` and `/dns/retrieve/researchstack.info`
|
|
|
|
---
|
|
|
|
## Post-Quantum Cryptography
|
|
|
|
| Layer | Status |
|
|
|-------|--------|
|
|
| **Tailscale** | X25519Kyber768 hybrid key exchange active (v1.98+) |
|
|
| **SSH (all nodes)** | `mlkem768x25519-sha256` preferred in `sshd_config` and `ssh_config` |
|
|
| **Garage RPC** | Tailscale transport only (no direct PQ on RPC layer) |
|
|
|
|
---
|
|
|
|
## Node Details
|
|
|
|
### qfox-1
|
|
|
|
| Spec | Value |
|
|
|------|-------|
|
|
| OS | Arch Linux, kernel 7.0.9-1-cachyos |
|
|
| CPU | AMD Ryzen (GPU compute available) |
|
|
| Disk | 1.8 TB NVMe |
|
|
| Memory | — |
|
|
| Tailscale | 100.88.57.96 |
|
|
| Garage | primary node, S3 endpoint |
|
|
| SSH config | `~/.ssh/config` entry `qfox-1` (local) |
|
|
|
|
### nixos-laptop
|
|
|
|
| Spec | Value |
|
|
|------|-------|
|
|
| OS | NixOS 26.05.20260521.f83fc3c (Yarara) |
|
|
| Disk | 459G NVMe, 394G free |
|
|
| Memory | 14 GiB total |
|
|
| Tailscale | 100.102.173.61 |
|
|
| k3s | v1.35.4+k3s1, single-node control plane |
|
|
| Authentik | Podman, port 9000 |
|
|
| Garage | storage node (ord zone) |
|
|
| zram | enabled in NixOS config |
|
|
|
|
### 361395-1 (Netcup VPS)
|
|
|
|
| Spec | Value |
|
|
|------|-------|
|
|
| OS | Debian 13 (OpenSSH_10.0p2) |
|
|
| Public IP | 46.232.249.226 |
|
|
| Disk | 125G, 66G free |
|
|
| Memory | — |
|
|
| Tailscale | 100.110.163.82 |
|
|
| Garage | storage node (fra zone) |
|
|
| zram | 2G zstd (manual `zramctl`) |
|
|
| APT issues | `enterprise.proxmox.com` returns 401; `pve-no-subscription` repo duplicated |
|
|
|
|
### microvm-racknerd
|
|
|
|
| Spec | Value |
|
|
|------|-------|
|
|
| OS | Debian (microVM) |
|
|
| Public IP | 172.245.19.182 |
|
|
| Disk | 9.1G, 6.4G free |
|
|
| Tailscale | 100.101.247.127 |
|
|
| Role | Caddy reverse proxy, public edge |
|
|
| Ports | 80, 443 open to internet |
|
|
|
|
### nixos-steamdeck-1
|
|
|
|
| Spec | Value |
|
|
|------|-------|
|
|
| OS | NixOS |
|
|
| Tailscale | 100.85.244.73 |
|
|
| Hostname | steamdeck |
|
|
| Role | GPU compute, planned edge LLM (3B-7B) |
|
|
| GPU | RDNA 2 |
|
|
| Status | just onboarded |
|
|
|
|
---
|
|
|
|
## Open Issues
|
|
|
|
| # | Issue | Node | Priority | Notes |
|
|
|---|-------|------|----------|-------|
|
|
| 1 | k3s Authentik PostgreSQL read-only filesystem | nixos-laptop | Low | Using standalone Podman instead |
|
|
| 2 | k3s Authentik Redis image pull failure | nixos-laptop | Low | Bitnami tags removed; Valkey already in use |
|
|
| 3 | 361395-1 APT 401 + duplicate repos | 361395-1 | Low | Cleanup `sources.list` when convenient |
|
|
| 4 | dracocomp offline | — | Low | Unreachable 3+ days |
|
|
| 5 | Garage S3 API only on qfox-1 localhost | qfox-1 | Low | Remote access via SSH tunnel or Tailscale funnel |
|
|
| 6 | Caddy admin API returns null certs JSON | racknerd | Info | Cert is functional; API introspection mismatch |
|
|
|
|
---
|
|
|
|
## Access Cheat Sheet
|
|
|
|
```bash
|
|
# SSH shortcuts (from ~/.ssh/config)
|
|
ssh nixos-laptop
|
|
ssh racknerd # alias for microvm-racknerd
|
|
ssh 361395-1
|
|
ssh steamdeck # nixos-steamdeck-1 (100.85.244.73)
|
|
|
|
# Tailscale direct
|
|
ssh -o StrictHostKeyChecking=accept-new root@100.110.163.82
|
|
|
|
# Authentik (local on nixos-laptop)
|
|
curl http://127.0.0.1:9000
|
|
|
|
# Caddy reload (on racknerd)
|
|
systemctl reload caddy
|
|
|
|
# Garage status
|
|
sudo /usr/local/bin/garage -c /etc/garage/garage.toml status
|
|
|
|
# Decrypt secrets
|
|
cd "/home/allaun/Research Stack"
|
|
sops --decrypt 4-Infrastructure/infra/secrets/credentials.json
|
|
|
|
# k3s on nixos-laptop
|
|
kubectl get pods -n services
|
|
kubectl get svc -n services
|
|
|
|
# restic backup (from qfox-1)
|
|
bash 4-Infrastructure/storage/restic/backup.sh full
|
|
```
|
|
|
|
---
|
|
|
|
## Changelog
|
|
|
|
| Date | Change |
|
|
|------|--------|
|
|
| 2026-05-22 | Redis replaced with Valkey in Authentik compose |
|
|
| 2026-05-22 | Caddy upstreams switched from k3s NodePort 30800 to standalone Podman 9000 |
|
|
| 2026-05-23 | **infra-controller** deployed on 361395-1 (Netcup); systemd timer every 5 min |
|
|
| 2026-05-23 | nixos-steamdeck-1 onboarded (100.85.244.73, NixOS, RDNA 2 GPU) |
|
|
| 2026-05-22 | nixos-laptop Tailscale IP changed 100.119.165.120 → 100.102.173.61 |
|
|
|
|
---
|
|
|
|
## Automation: infra-controller on 361395-1
|
|
|
|
The **infra-controller** is the central health orchestration daemon running on the Netcup VPS (361395-1). It probes all nodes every 5 minutes via SSH over the Tailscale mesh.
|
|
|
|
### Architecture
|
|
|
|
```
|
|
361395-1 (Netcup) — CONTROL PLANE
|
|
├── infra-controller.timer (systemd, every 5 min)
|
|
├── infra-controller.service (oneshot)
|
|
├── Receipts: ~/.cache/infra-controller.jsonl (hash-chained)
|
|
└── Alerting: local postfix → admin@researchstack.info
|
|
|
|
Probes (SSH → each node):
|
|
qfox-1 → system, restic, garage, garage_buckets, gdrive_offload
|
|
nixos-laptop → system, k3s
|
|
microvm-racknerd → system, caddy
|
|
nixos-steamdeck-1 → system
|
|
361395-1 (local) → system, garage, garage_buckets
|
|
|
|
Roles (affects alert severity):
|
|
CRITICAL: microvm-racknerd, nixos-laptop → alert on down
|
|
OPTIONAL: qfox-1, nixos-steamdeck-1 → log only
|
|
```
|
|
|
|
### Receipts
|
|
|
|
- **Schema:** `infra_controller_receipt_v1`
|
|
- **Format:** JSONL hash-chain, one entry per cycle
|
|
- **Local:** `~/.cache/infra-controller.jsonl` (always available)
|
|
- **S3 backup:** planned (s3://research-stack/agent-receipts/)
|
|
|
|
### Commands
|
|
|
|
```bash
|
|
# Manual run
|
|
ssh 361395-1 "cd '/home/allaun/Research Stack' && python3 4-Infrastructure/auto/infra_controller.py --probe-only"
|
|
|
|
# View latest receipt
|
|
ssh 361395-1 "tail -1 ~/.cache/infra-controller.jsonl | python3 -m json.tool"
|
|
|
|
# Watch journal
|
|
ssh 361395-1 "journalctl -u infra-controller.service -f"
|
|
|
|
# Timer status
|
|
ssh 361395-1 "systemctl status infra-controller.timer"
|
|
```
|
|
|
|
### Code location
|
|
|
|
- `4-Infrastructure/auto/infra_controller.py` — main daemon
|
|
- `4-Infrastructure/auto/lib/probe.py` — SSH probe runner
|
|
- `4-Infrastructure/auto/lib/receipt.py` — receipt schema + hash-chain
|
|
- `4-Infrastructure/auto/lib/alerting.py` — email/webhook/dashboard dispatch
|
|
- `4-Infrastructure/auto/lib/config.py` — YAML config loader
|
|
- `4-Infrastructure/auto/config/nodes.yaml` — node inventory + thresholds
|
|
- `4-Infrastructure/auto/nodes/*.sh` — per-probe collector scripts (bash)
|
|
|
|
| 2026-05-21 | Wildcard TLS `*.researchstack.info` deployed via Porkbun DNS-01 |
|
|
| 2026-05-21 | Porkbun API keys regenerated and re-encrypted |
|
|
| 2026-05-20 | Garage cluster bumped to `replication_factor=3` across all nodes |
|
|
| 2026-05-20 | zram enabled cluster-wide |
|
|
| 2026-05-19 | NixOS flake conversion + k3s bootstrap on nixos-laptop |
|
|
| 2026-05-18 | Garage buckets created: research-stack, db-scratch, rds-overflow, snap-zone, gdrive-mirror |
|