foundation/documentation/000_TOPOLOGY.md
Andreas Niemann f18676e6b3 chore: scaffold olsitec-foundation mono-repo
Repo topology, baseline overlay, planning docs (PLAN-001/002), ADR-004/005,
and the bootstrap/packages/documentation skeleton. Implementation (T00+) not started.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 17:10:46 +02:00

153 lines
8.9 KiB
Markdown

# 000 — `olsitec-foundation` Repository Topology
> **Purpose**: define what lives where under `~/work/olsitec-foundation`, which directories are
> independent git repositories vs. one mono-repo, and how shared Pulumi modules are vendored now and
> consumed from the foundation later.
> **Status**: Active. **Companion**: [ADR-005](decisions/ADR_005_repo_topology.md),
> [PLAN-002](planning/PLAN-002-foundation-implementation.md).
---
## 1. The workspace root is NOT a git repo
`~/work/olsitec-foundation/` is a **workspace root**, following the Olsitec convention (baseline
PD-2: "the workspace root is usually not a git repository"). It holds **two** independent git
repositories:
```
~/work/olsitec-foundation/ # workspace root — NOT a git repo
├── foundation/ # GIT REPO #1 — the platform mono-repo (the DR unit)
└── ai-baseline/ # GIT REPO #2 — the cross-project agentic workflow pattern
```
Both repos are hosted in **foundation-Forgejo** once it exists
(`forge.olsitec.de/olsitec/foundation`, `forge.olsitec.de/olsitec/ai-baseline`). Until then they
live locally + an offsite git mirror (DR).
---
## 2. Why two repos (the mono-repo vs single-repo decision)
| Repo | Kind | Why this boundary |
|------|------|-------------------|
| **`foundation`** | **Mono-repo** (Bun workspaces) | The egg (`bootstrap/`) and the shared Pulumi modules it needs (`packages/`) **must be co-versioned and present without a registry at day-zero** (the registry doesn't exist yet — bootstrap paradox). One `git clone` = the full DR unit. Project docs travel with it. |
| **`ai-baseline`** | **Single small repo** | Consumed by **every** Olsitec project (foundation + all Layer-1 products), on an **independent release cadence**. It should not drag the platform's weight, and ADR-003 already specced it as standalone. |
**What is deliberately NOT in here:** downstream **consumer projects** (the existing olsicloud4 K8s
platform, products like seaspots, olsitrack2…). They are their own repos and consume the
foundation's **published** packages — they do not vendor or sit inside this workspace.
> Rule of thumb: **co-version what must boot together (mono-repo); separate what is consumed on its
> own cadence (its own repo + published artifact).**
---
## 3. `foundation` mono-repo layout
```
foundation/ # GIT REPO #1 (mono-repo, Bun workspaces)
├── package.json # workspace root (defines packages/* + bootstrap)
├── VERSIONS # pinned image+tool digests (determinism)
├── README.md
├── bootstrap/ # ── THE EGG ── single Pulumi project (DR unit core)
│ ├── Pulumi.yaml
│ ├── Pulumi.foundation.yaml # passphrase-encrypted config + secrets
│ ├── index.ts # phase orchestration
│ ├── components/ # network, postgres, rustfs, vault, credentials, proxy, forgejo, runner
│ ├── phases/ lib/ config.ts
│ └── config/ # template SOURCES (app.ini.tmpl, Caddyfile.tmpl, pg-init.sql)
├── packages/ # ── SHARED PULUMI MODULES ── each independently publishable
│ ├── pulumi-docker/ # @olsitec/pulumi-docker
│ ├── pulumi-vault/ # @olsitec/pulumi-vault
│ ├── pulumi-tls/ # @olsitec/pulumi-tls
│ ├── pulumi-hetzner/ # @olsitec/pulumi-hetzner
│ ├── pulumi-cloudflare/ # @olsitec/pulumi-cloudflare
│ ├── pulumi-rustfs/ # @olsitec/pulumi-rustfs (new)
│ ├── pulumi-postgres/ # @olsitec/pulumi-postgres (new)
│ ├── pulumi-forgejo/ # @olsitec/pulumi-forgejo (new)
│ ├── pulumi-caddy/ # @olsitec/pulumi-caddy (new)
│ └── pulumi-runner/ # @olsitec/pulumi-runner (new)
├── preflight/ # tooling/host validation
├── backup/ dr/ # backup + disaster-recovery automation
├── .forgejo/workflows/ # CI (preflight, pulumi preview/up, backup-verify)
└── documentation/ # ── PROJECT DOCS ── (this folder)
├── 000_TOPOLOGY.md # this file
├── 000_baseline.md # thin overlay → ai-baseline + foundation deviations
├── planning/ # PLAN-001 (vision), PLAN-002 (strategy)
├── decisions/ # ADRs (004 layered platform, 005 topology, …)
├── contracts/ agents/ sessions/ knowledge_base/ retrospectives/ _templates/
```
### Why `bootstrap/` and `packages/` share one repo
Day-zero, the egg cannot pull `@olsitec/pulumi-vault` from a registry — **the registry is part of
what it is building**. With Bun workspaces, `bootstrap/` resolves `@olsitec/pulumi-*` from
`packages/*` **locally on disk**. No registry needed to bootstrap. This is the concrete resolution of
the "registry hosts the modules that build the registry" paradox (PLAN-002 §5.2).
---
## 4. `ai-baseline` repo layout
```
ai-baseline/ # GIT REPO #2
├── 000_baseline.md # the CANONICAL full agentic baseline (single source of truth)
├── _templates/ # ADR, contract, session, snapshot templates
└── README.md # what this is + how projects consume it
```
This is ADR-003's `ai-baseline`, **re-homed from gitlab.com to foundation-Forgejo**. Every project's
`documentation/000_baseline.md` becomes a **thin overlay** that references this canonical file and
lists only project-specific deviations.
---
## 5. Module vendoring & distribution strategy
The lifecycle of a shared module has **three stages**:
```
STAGE 1 VENDOR (now) Copy the existing olsicloud4 module → foundation/packages/pulumi-<x>/.
Pin it. bootstrap/ consumes it via Bun workspace (local, no registry).
STAGE 2 PUBLISH (after egg) Once foundation-Forgejo npm registry is live, CI publishes each package
as @olsitec/pulumi-<x>@<semver> to it (semantic-release-monorepo,
Conventional Commits — see memory: olsitec-charts-conventional-commits).
STAGE 3 CONSUME (steady) Downstream projects switch their imports from the old
olsicloud4/pulumi/modules/<x> to @olsitec/pulumi-<x>@<version> pulled
from the foundation registry. The old modules are frozen, then removed.
```
This is exactly the user's intent: *"a copy placed there… later hosted via the foundation… existing
uses of vault modules will then use the module hosted from the foundation."*
### 5.1 Mapping: existing olsicloud4 module → foundation package
| `olsicloud4/pulumi/modules/<x>` | → `foundation/packages/` | Day-zero need | Notes |
|---|---|---|---|
| `docker` | `pulumi-docker` | **Yes** | Egg orchestration (`@pulumi/docker` over SSH). Reuse `DockerDeployments` wrapper. |
| `vault` | `pulumi-vault` | **Yes** | Includes `policy.ts`. Core of the secret layer. |
| `tls` | `pulumi-tls` | Maybe | Cert helpers; may defer to Caddy/Vault-PKI. |
| `hetzner` | `pulumi-hetzner` | Phase 0 | VM provisioning + offsite host. |
| `cloudflare` | `pulumi-cloudflare` | Networking | DNS records + ACME DNS-01 token. |
| `olsitec` | `pulumi-olsitec` | Partial | The `OlsitecProject` feature-flag component (ADR-002). Layer-1 oriented (K8s); the egg uses a lighter subset. Vendor for reference, refactor for Layer 0. |
| `minio` | — (superseded) | No | Foundation uses **RustFS**, not MinIO → new `pulumi-rustfs`. |
| `gitlab` | — (retired) | No | GitLab is what the foundation **replaces**. |
| `kubernetes`, `k3s`, `libvirt`, `baremetal` | — (Layer 1) | No | Belong to Layer-1 / legacy provisioning, not the egg. |
### 5.2 New packages (no existing module)
`pulumi-rustfs`, `pulumi-postgres`, `pulumi-forgejo`, `pulumi-caddy`, `pulumi-runner` — authored fresh
as foundation tasks (PLAN-002 §10: T03/T04/T05/T07/T08/T10).
> **Do not copy module code yet.** Stage-1 vendoring of `docker` + `vault` (+ `hetzner`, `cloudflare`)
> is a deliberate task with its own commit, to be done when T00 starts — not blindly bulk-copied with
> `node_modules`. This doc defines the destinations; the copy is gated on the contracts (T00).
---
## 6. DR implication
The DR unit is **the `foundation` mono-repo + master passphrase + offsite backup bundle** (PLAN-002
§6). Because `packages/` is inside the mono-repo, a single clone restores both the egg and the exact
module sources it was built from — no registry, no external module fetch required to recover.
`ai-baseline` is operationally useful but **not** required to recover the platform.