# Session 2026-06-30 #001 — Foundation bootstrap: contracts → live VM + DNS + offsite backup ## What was done - **T00** contracts CONTRACT_001–004 (+ amendment for `hosts.git`, `vm.sshPort`, `cloudflare.zoneId`). - **T01** `preflight/` + `VERSIONS` (images carry `:PIN_DIGEST` placeholders — see gotchas). - **T02** Bun-workspace + typed `config.ts` + vendored `@olsitec/pulumi-{docker,vault}`. - **ADR-006 precursor**: shared Docker-over-SSH provider + `foundation-net` (`lib/context.ts`, `components/network.ts`, phase-slotted `index.ts`). Composition = per-component factories, gates via `dependsOn`. - **Phase-0 VM**: vendored `@olsitec/pulumi-hetzner`; `provision/` stack. Live: **cx33 Helsinki, `204.168.234.72`**, Docker 29.6.1, **SSH port 222**, key `~/.ssh/foundation-test_ed25519`, firewall 222/80/443/2222. - **Offsite backup**: `offsite-backup/` stack — bucket **`olsitec-foundation`** + scoped service account on `minio.wob.olsitec.de:19000` (home Synology). Verified scoped (cross-bucket denied). - **Steps 1+2**: real `olsitec.net` config + **DNS** `forge/vault/s3/git.olsitec.net → 204.168.234.72` (verified authoritative; explicit A shadows the `*.olsitec.net` wildcard). `foundation-net` live on VM. ## Current state - Repo: `~/work/olsitec-foundation/foundation`. Latest commit `185be52`. Working tree clean. - **Master passphrase** (the one external secret): `pass olsitec-foundation/PULUMI_CONFIG_PASSPHRASE`. - Deploy the foundation: `cd bootstrap && ./run.sh up` (sets passphrase + `SSH_PRIVATE_KEY_PATH`). - Stacks (all local file backends): `bootstrap` stack **foundation** (real passphrase); `provision` stack **foundation-test** + `offsite-backup` stack **prod** (throwaway passphrase `dev-validation-throwaway`). - Other creds: `pass olsicloud4/{HCLOUD_TOKEN,CLOUDFLARE_MASTER_TOKEN}`; MinIO admin via the `wob-admin-external` mc alias (accessKey `admin`, NOT `pass MINIO_BACKUP_*` which is a different instance). ## Next steps (Wave 2 → the goal: `git clone git@git.olsitec.net`) Validate each live on the VM via `bootstrap/run.sh up` (images still unpinned → `FOUNDATION_ALLOW_UNPINNED=1` or pin digests per `VERSIONS` header). Each component is a factory `components/.ts`, wired into the `index.ts` phase slots (ADR-006). 1. **T03 postgres** · **T04 rustfs** (+ buckets) — data plane. 2. **T05 vault** — sealed container + init-capture (reuse `olsitec-core/run.sh` pattern → unseal keys to `vaultCredentials:*` encrypted config) + passphrase-gated unseal helper (ADR-004). The hard one. 3. **T06 credentials** — `@pulumi/random` → Vault (CONTRACT_002 paths). 4. **T07 Caddy** — DNS-01 TLS; needs a **Cloudflare-enabled Caddy image** (build via xcaddy; standard `caddy:2` lacks the DNS plugin). Token already in `foundation:cloudflareApiToken`. 5. **T08 Forgejo** — app.ini ← Postgres/RustFS/Vault; first admin headless; **org + repo** so `git clone git@git.olsitec.net:olsitec/...` works (forgeSshPort 2222, published). 6. **T10 runner** · **T12 backup** (RustFS → replicate to the offsite `olsitec-foundation` bucket; scoped creds already in `foundation:backup.offsite*`). ## Risks / watchouts - `Pulumi.foundation.yaml` self-quotes scalars + `encryptionsalt` is now **load-bearing** (decrypts 3 committed secrets) — keep it committed; don't strip the salt anymore. - VM SSH is **port 222**; the eventual real VM would be 22 (`vm.sshPort` handles it). - `VERSIONS` images are `:PIN_DIGEST` + RustFS is `latest` — pin before treating as production. - Don't run `pulumi up` against the production `olsicloud4-*` stacks. The home MinIO `.local` name is LAN-only; the VM must use the public `minio.wob.olsitec.de:19000`. - Everything is destroyable: `provision` (VM), `offsite-backup` (bucket+creds) via `pulumi destroy`. ## Operating mode for next session: HIGH-RISK / INFRA (remote VM, Docker, secrets).