From f1c1c79b63236187a9fc1e212f7007b0a49f970a Mon Sep 17 00:00:00 2001 From: Lars Rossen Date: Sat, 22 Aug 2026 16:47:53 +0200 Subject: [PATCH] slides(tappaas): write the OrangeMaker podman session deck Sixteen slides built around the live demo: two orientation diagrams (site shape with environments/zones/satellite, and the pull-based GitOps repo topology), a condensed module anatomy anchored on the real podman.json from Community/src/larsrossen/containers/podman, the three demo beats (test environment, module add, prove it), and the identity hand-out. Mermaid subgraph titles collide with the node row under flowchart TB, so both diagrams use LR; _class directives repeat 'invert' because a local _class replaces the deck-level one rather than adding to it. --- slides/tappaas/how-to/new-module/index.md | 347 +++++++++++++--------- 1 file changed, 202 insertions(+), 145 deletions(-) diff --git a/slides/tappaas/how-to/new-module/index.md b/slides/tappaas/how-to/new-module/index.md index 57779c9..6ab84c2 100644 --- a/slides/tappaas/how-to/new-module/index.md +++ b/slides/tappaas/how-to/new-module/index.md @@ -4,229 +4,286 @@ theme: gaia class: invert paginate: true title: How to implement a TAPPaaS module -description: Walk-through of adding a new module to the TAPPaaS repository +description: Live build of a Podman module — OrangeMaker session --- -# How to implement a TAPPaaS module +# Getting a module onto TAPPaaS -From empty directory to merged pull request +Live build of a **Podman** container host + +OrangeMaker · `TODO: date` --- -## Who this is for +## The plan -- You want to add an **app** or a **foundation** capability to TAPPaaS -- You can read shell, JSON, and a little Nix -- You have a TAPPaaS test environment you are allowed to break +1. **What TAPPaaS is** — two diagrams, ten minutes, no deeper +2. **What a module actually is** — a json contract and three scripts +3. **Build one live** — a test environment, then `podman` into it +4. **Prove it works** — tests, then the web console +5. **You get a login** — your own identity on this system -**Prerequisites** - -- A clone of `TAPPaaS/TAPPaaS`, branch `TODO: ADR007 or stable` -- SSH access to a test site -- `TODO: tooling list` +You leave with: a mental model, a module you watched get built, and an account. --- -## Vocabulary in 60 seconds + -| Term | Means | -| --- | --- | -| **Module** | One installable unit under `src/apps/` or `src/foundation/` | -| **Service** | A part of a module with its own lifecycle scripts | -| **Manager** | Owns desired state for a domain | -| **Controller** | Applies desired state to one concrete technology | - -`TODO: tighten against ADR-007 taxonomy; drop the terms that do not matter here` - ---- - -## Anatomy of a module - -```text -src/apps// -├── README.md # what it is, why it exists -├── DESIGN.md # how it is put together -├── INSTALL.md # operator-facing install notes -├── UPGRADE.md # operator-facing upgrade notes -├── AUTHORS.md # who to ask -├── LICENSE -├── .json # declaration: the module's contract -├── .nix # runtime definition -├── install.sh # create it -├── update.sh # move it forward -├── test.sh # prove it works -└── services// # per-service install/update/test/delete -``` - ---- - -## The path from nothing to merged +## The shape of a TAPPaaS site ```mermaid flowchart LR - A[Scaffold from
00-Template] --> B[Declare
module.json] - B --> C[Define runtime
module.nix] - C --> D[install.sh] - D --> E[services/] - E --> F[update.sh] - F --> G[test.sh] - G --> H[Docs] - H --> I[PR + CI] + sat["satellite VPS
public IP · ingress
off-site backup"] + subgraph z0["mgmt · zone mgmt"] + cicd["tappaas-cicd"] + net["network"] + clu["cluster"] + bak["backup"] + idp["identity"] + end + subgraph z1["default · zone srv"] + nc["nextcloud"] + ha["home-assistant"] + end + subgraph z2["test · zone hacklab"] + pod["podman — today"] + end + sat --> net + cicd --> nc + cicd --> pod ``` --- -## Step 1 — Scaffold +## The words we will use + +| Word | Meaning | +| --- | --- | +| **Module** | The smallest deployable unit — its own VM, its own json contract. Foundation *and* apps are modules. | +| **Environment** | A tenant. `mgmt` and one named after the site always exist; add more freely. | +| **Zone** | A VLAN with a firewall policy. Modules land in their environment's zone. | +| **Mothership** | `tappaas-cicd` — where the managers run and where you type. | +| **Satellite** | Optional VPS, for sites with no usable public IP. | + +Everything is a module. There is one model to learn, not eight. + +--- + + + +## How code gets in: pull-based GitOps + +```mermaid +flowchart RL + subgraph t["TAPPaaS repository"] + stable["stable"] + main["main"] + end + subgraph c["Community repository"] + cm["main"] + end + subgraph inst["Your TAPPaaS · tappaas-cicd"] + lt["clone of TAPPaaS"] + lc["clone of Community"] + end + lt -->|pull| stable + lc -->|pull| cm +``` + +Nothing pushes into your site. It pulls, on a schedule, and reconciles. + +--- + +## Managers: the verbs you will watch me type ```bash -cp -r src/apps/00-Template src/apps/ +module-manager module add|test|reconcile|modify|delete +environment-manager add|modify|reconcile +network-manager zone add|merge|reconcile +people-manager user add|modify · reconcile --apply ``` -- Rename `template.json` → `.json`, `template.nix` → `.nix` -- Read `README-template.md`, then delete it -- `TODO: is there a scaffold script? if not, should there be?` +Admins drive **verbs**, never hand-edited JSON. Each manager owns one slice of +desired state; controllers push it into OPNsense, Proxmox, the switch, Authentik. --- -## Step 2 — Declare the module +## A module is a contract and three scripts -`.json` +```text +podman/ +├── podman.json # the contract — what it is, needs, provides +├── install.sh # put the software in the VM (once) +├── update.sh # keep it patched (on schedule) +├── test.sh # prove it still works (gates updates) +├── README.md # what it is +└── INSTALL.md # what automation cannot do for you +``` + +That is the whole surface. Everything else is the platform's job. + +--- + +## The contract, for real ```json { - "TODO": "smallest working declaration" + "description": "Podman container host — Debian 13 VM with rootless Podman + Cockpit", + "vmname": "podman", "vmid": 812, + "dependsOn": ["cluster:vm", "templates:debian", "backup:vm", "network:proxy"], + "config": { + "cluster:vm": { "cores": 2, "memory": "2048", "diskSize": "20G", + "image": "debian-13-generic-amd64.qcow2" }, + "network:proxy": { "proxyPort": 9090, "proxyUpstreamTls": true, + "proxyUpstreamHttp1": true, "proxyAllowedZones": ["mgmt"] } + } } ``` -- What the module *is*, not how it is built -- `TODO: required keys, optional keys, validation` +`dependsOn` is the whole trick: four services, declared — not scripted. --- -## Step 3 — Define the runtime +## The three scripts, honestly -`.nix` +| Script | Called | Does | +| --- | --- | --- | +| `install.sh` | once, at add | For podman: `exec update.sh` — install *is* update here | +| `update.sh` | every schedule | apt: `podman`, `podman-compose`, `cockpit`, `cockpit-podman`; enable the rootless socket | +| `test.sh` | on demand + before every update merge | VM reachable · podman present · plugin present · console answers on `:9090` | -```nix -# TODO: minimal example -``` - -- `TODO: what belongs in nix vs. what belongs in install.sh` +If `test.sh` is honest, unattended updates are safe. That is the entire deal. --- -## Step 4 — `install.sh` +## What you did not have to write -- Idempotent: safe to run twice -- Fails loudly, never half-way -- `pre-install.sh` when something must exist first +- A VM — `cluster:vm` built it from the Debian 13 cloud image +- OS prep — `templates:debian` did apt + guest agent +- A VLAN, an interface, DHCP, firewall rules — the zone came with the environment +- `https://podman.` with a real certificate — `network:proxy`, one dependency +- Nightly backup to PBS — `backup:vm` +- Scheduled updates and health reporting — the mothership + +Six lines of json bought all of it. + +--- + +## Demo 1 — an environment of our own ```bash -# TODO: skeleton with the house conventions +# srvTest ships in the zone template; a fresh one keeps the demo self-contained +network-manager zone add hacklab --from-zone srv + +environment-manager add test \ + --display "Hackerspace test" \ + --zone hacklab \ + --domain test. ``` +A tenant with its own VLAN, its own firewall posture, its own DNS names — +and nothing in it can touch production. + +`TODO: our domain — and do we build the zone live or pre-bake it?` + + + --- -## Step 5 — Services +## Demo 2 — install the module + +```bash +module-manager module add podman --environment test +``` + +Watch the order: dependencies resolve first, then the VM, then the network, then +`install.sh`. Install order is **computed** from every module's `dependsOn` — nobody +maintains a list. + + + +--- + +## Demo 3 — prove it, then look at it + +```bash +module-manager module test podman +``` + +Then open **`https://podman.`** — Cockpit, with the Podman page. + +- Reachable from the `mgmt` zone only. Not from the internet, by declaration. +- Cockpit authenticates against **Linux accounts on the VM** — the cloud-init + `tappaas` user has no password until someone sets one. + +`TODO: pre-create the demo login, or set the password live?` + +--- + +## Where this module lives ```text -services//install-service.sh -services//update-service.sh -services//test-service.sh -services//delete-service.sh +Community/src/larsrossen/containers/podman/ ``` -- One service = one thing with its own lifecycle -- `TODO: when to split into services vs. keep it in the module` +Three homes for a module, all first-class: ---- - -## Step 6 — `update.sh` - -- Upgrade in place, no data loss -- Version pinning: `TODO` -- Rollback story: `TODO` - ---- - -## Step 7 — `test.sh` - -- Runs against a real environment, not a mock -- Exit non-zero on failure, quiet on success -- `TODO: what CI runs, and what only a human can check` - ---- - -## Step 8 — Documentation - -| File | Answers | +| Home | For | | --- | --- | -| `README.md` | What is this and why would I install it? | -| `DESIGN.md` | How is it built, what were the trade-offs? | -| `INSTALL.md` | How do I install it, step by step? | -| `UPGRADE.md` | What breaks between versions? | +| **TAPPaaS** repo, by pull request | modules the whole project should carry | +| **Community** repo | yours, shared, no gatekeeping — today's podman | +| **Private** repo | yours, not shared | -Docs are synced to the docs site — a missing README is a missing page. +Adding a repository is one `site-manager repository add`. --- -## Try it on a test site +## Your turn — identity on this TAPPaaS ```bash -# TODO: the real loop +people-manager user add --email @example.org \ + --roles user --groups orangemaker__users + +people-manager reconcile # preview +people-manager reconcile --apply # push to Authentik ``` -1. Deploy to the test environment -2. Run `test.sh` -3. Break it on purpose, run it again +One login per person, roles via groups. `TODO: which groups/roles do participants get?` + + --- -## Submit +## Take it further -- Branch: `TODO: naming convention` -- One module per pull request -- CI must be green -- Review checklist: - - [ ] Idempotent install - - [ ] `test.sh` passes on a clean site - - [ ] Docs complete - - [ ] `TODO` +- **Develop a module** — tappaas.org/generated/develop-a-module +- **Every json field** — tappaas.org/generated/schemas +- **Zones and the access model** — tappaas.org/generated/zones +- **Repository topology** — tappaas.org/generated/design/cicd-git +- **This deck** — slides.makerfloss.eu/tappaas/how-to/new-module ---- - -## Pitfalls - -- `TODO: the three mistakes reviewers actually see` -- `TODO` -- `TODO` - ---- - -## Where to go next - -- `src/apps/00-Template/` — the canonical starting point -- `docs/ADR/ADR-007 - TAPPaaS Taxonomy.md` -- `TODO: contribution guide link` - -**Questions?** +Pick an app you want on a sovereign box. Open an issue first — someone may already +be packaging it.