diff --git a/docs/presentations/2026-06-28-tappaas-vps-publishing.md b/docs/presentations/2026-06-28-tappaas-vps-publishing.md
new file mode 100644
index 0000000..b4eaa8c
--- /dev/null
+++ b/docs/presentations/2026-06-28-tappaas-vps-publishing.md
@@ -0,0 +1,170 @@
+---
+marp: true
+theme: gaia
+class: invert
+paginate: true
+---
+
+
+
+# Routing TaPPaaS through the VPS
+
+### Split-horizon DNS · public exposure on a static IP
+
+MakerFLOSS · June 2026 · _technical review_
+
+---
+
+## The goal
+
+Publish selected **TaPPaaS** web services under `*.tappaas.makerfloss.eu`.
+
+- **Public exposure** — A records point at the VPS static IP `88.99.32.236`,
+ so the cluster's services are reachable from anywhere.
+- **Split-horizon DNS** — the _same_ hostname resolves to a **local** address
+ for internal clients, so they never round-trip to the VPS.
+- **Isolation preserved** — nothing on the makerspace side egresses through
+ the VPS or reaches the homelab.
+
+> We are not inventing anything — we clone a pattern already running.
+
+---
+
+## Building block: the proven `mf01` pattern
+
+`mf01` already publishes `*.mf01.makerfloss.eu` exactly this way, live since
+2026-06-09:
+
+- TLS terminates on the **VPS** (wildcard cert, Gandi DNS-01).
+- Plain HTTP rides the **`wg1`** WireGuard tunnel to an internal reverse proxy.
+- The internal proxy routes by **Host** to the right container.
+
+**TaPPaaS = the same shape**, with Caddy as the internal proxy instead of an
+internal Traefik. Low risk, known gotchas already solved.
+
+---
+
+## External request flow
+
+```mermaid
+flowchart LR
+ C["Browser
(off-site)"] -->|"https · *.tappaas
DNS → 88.99.32.236"| T["VPS Traefik :443
wildcard cert
TLS ends here"]
+ T -->|"plain HTTP
inside wg1"| K["FLOSSFirewall / Caddy
10.13.0.9:80"]
+ K -->|"route by Host"| S["TaPPaaS service
srv01 / srv02 / srv03"]
+```
+
+TLS terminates at the VPS; the backend hop is plain HTTP but **encrypted on the
+wire inside WireGuard**.
+
+---
+
+## Internal flow — two views, one name
+
+```mermaid
+flowchart TD
+ N["<svc>.tappaas.makerfloss.eu"]
+ N -->|"public DNS (Gandi)"| V["VPS 88.99.32.236
VPS wildcard cert"]
+ N -->|"internal DNS (FLOSSFirewall)"| L["Caddy local IP
Caddy's own LE cert"]
+```
+
+A client only ever sees the cert for the IP it resolved — **VPS wildcard
+externally, Caddy's own cert internally**. The two views never collide.
+
+---
+
+
+
+## Key decisions
+
+| Decision | Choice | Why |
+|---|---|---|
+| Transport | New **`wg1`** WireGuard peer | Mirrors mf01; both ends self-owned; static hub-and-spoke |
+| TLS (external) | **Terminate at the VPS** | Cert issuance while isolated kills passthrough; Gandi DNS-write key stays on the VPS; keeps edge routing/middleware |
+| Naming | **Wildcard** `*.tappaas.makerfloss.eu` | One cert + one route; add services with zero VPS change; no apex clash (`forgejo.tappaas…` ≠ `forgejo.makerfloss.eu`) |
+| Internal DNS | **FLOSSFirewall** (already runs DNS), cluster first | LAN view deferred — depends on third-party router |
+
+---
+
+## Components — VPS side
+
+All additive in `AnsibleBaobabV4/host_vars/makerfloss.yml`, mirroring mf01:
+
+1. **wg1 peer** — FLOSSFirewall, `allowed_ips: 10.13.0.9/32`.
+2. **Wildcard cert** — add `tappaas.makerfloss.eu` + `*.tappaas` to
+ `traefik_wildcard_sets` → ACME anchor issues it via Gandi DNS-01.
+3. **Catch-all route** — `tappaas-delegate.yml`:
+ `HostRegexp(...tappaas...)` → `http://10.13.0.9:80`, `passHostHeader: true`.
+4. **Public DNS** — `tappaas` + `*.tappaas` A → `88.99.32.236`.
+
+After this, **new services need zero VPS change** — exposure is decided at Caddy.
+
+---
+
+## Components — TaPPaaS / Caddy side
+
+1. **WireGuard client** — peer to the VPS hub, interface `10.13.0.9/32`,
+ `AllowedIPs = 10.13.0.0/24` only → **split-tunnel** (no general egress
+ through the VPS).
+2. **Caddy plain-HTTP backend** — Host-routed listener on the wg interface
+ (`10.13.0.9:80`), **HTTPS-redirect OFF**. Caddy keeps serving `:443` with its
+ own certs to internal clients, unchanged.
+3. **Firewall** — allow `tcp/80` **only from `10.13.0.1`** (the VPS) on the wg
+ interface.
+
+---
+
+## Phasing — five verifiable steps
+
+1. **Tunnel** — FLOSSFirewall up as `wg1` peer; ping `10.13.0.1 ↔ 10.13.0.9`.
+2. **Caddy backend** — from the VPS,
+ `curl -H 'Host: .tappaas.makerfloss.eu' http://10.13.0.9:80`.
+3. **VPS edge** — add cert + route + DNS; off-site
+ `curl https://.tappaas.makerfloss.eu` with a valid cert.
+4. **Internal DNS** — add `*.tappaas` override; a cluster node resolves to
+ Caddy's local IP and gets Caddy's own cert.
+5. **(Later)** makerspace LAN view — conditional-forward + firewall pinhole on
+ the OrangeMakers router.
+
+---
+
+## Isolation — the hard requirement
+
+- **Split-tunnel** (`AllowedIPs = 10.13.0.0/24`) — the cluster never egresses
+ through the VPS; nothing on `wg1` reaches the makerspace or homelab.
+- VPS→Caddy locked to `tcp/80 from 10.13.0.1` on the wg interface.
+- Backend hop encrypted inside WireGuard.
+- **No public-DNS-write credential leaves the VPS** — the reason TLS terminates
+ there, not via passthrough.
+
+---
+
+## Risks & open items
+
+- **Caddy redirect** — the wg-interface listener must _not_ force HTTPS-redirect,
+ or the VPS hits a loop (mf01's known gotcha).
+- **YAML safety** — a syntax error in `tappaas-delegate.yml` breaks the whole
+ VPS file provider; keep the regex single-quoted.
+- **`10.13.0.9` free?** — confirm before assigning.
+- **TaPPaaS config repo TBD** — the FLOSSFirewall/Caddy/DNS config home is not
+ yet identified.
+- **Phase 5** depends on OrangeMakers router cooperation.
+
+---
+
+## Summary & next steps
+
+- Reuse the **proven mf01 pattern** — terminate TLS at the VPS, proxy over
+ `wg1` to TaPPaaS Caddy, split-horizon DNS for the internal view.
+- VPS side is a small, additive, **zero-new-tech** change.
+- **Next:** identify the TaPPaaS config repo, then write the implementation plan
+ and execute phases 1–4.
+
+_Design: `MakerFLOSS_Troubleshooting/docs/superpowers/specs/2026-06-28-tappaas-vps-publishing-design.md`_
+