Skip to content

Roadmap

What we are building, in the order we build it, and what each release has delivered — the open work at the top, the releases below it.

The current plan is security and stability. It is built in waves: first make upgrades safe, then make the breaking changes while few sites depend on today's shapes, then harden the network, then stability, then new capabilities. A filled marker means work in the wave is done and waits for the next release; a hollow one, nothing in it is done yet. Every two weeks the next version is cut for staging and reaches production two weeks later. Press + on a card for its groups and their issues.

Wave 7

Enhance the stacks

New modules and significant upgrades of existing ones: what a site can run, beyond the foundation and its fixes.

2 groups 3 open 4 done for 2.3
Groups and issues

G7.1 DevOps stack

Forgejo gets its CI: a Woodpecker module wired to it (#840), whose agents can also build TAPPaaS itself (#415), and Forgejo on PostgreSQL when load asks for it (#284).

  • #840 devops: a Woodpecker CI module next to Forgejo 2.3
  • #415 Self-hosted KVM Woodpecker runners
  • #284 forgejo: SQLite → PostgreSQL

G7.2 New and reworked app modules

New modules — a Markdown website (#841), N.O.M.A.D. for the home stack (#129), Karakeep for bookmarks (#842) — and a rework of LiteLLM's control plane (#170, decided with #430).

  • #841 A lazysite module: a Markdown website for the site owner 2.3
  • #129 Project N.O.M.A.D. in the home stack 2.3
  • #842 A Karakeep module: bookmarks and read-later, AI tagging via the AI stack 2.3
  • #170 LiteLLM declarative control plane
Wave 6

Site fabric and user experience

The physical site and how an operator sees and runs it. Not ordered by upgrade risk like Waves 0–4: these two groups are designed together — the fabric is what the physical view shows and changes.

3 groups 13 open 23 done for 2.3
Groups and issues

G6.1 Switching fabric, WAN, ISP & site operations

The links a site stands on: node uplinks (a bond under lan, #40), switch ports and trunks kept in line with the zones (#668, #697, #706, #731), the WAN and the ISP (public DNS that follows the WAN, #383, #830; mail when the ISP blocks port 25, #125). Switch setup needs a systematic model, at install and on day 2. Operating the site belongs here too: a service desk (#63) and a SIEM (#843, Wazuh the best candidate). Linked to G6.2: the physical view shows and later changes it.

  • #40 Redundant lan uplinks: a bond under the lan bridge 2.3
  • #383 Keep wildcard public DNS current on dynamic WAN
  • #830 environment-manager: no verb creates the public DNS record
  • #125 Mail relay via M365 Graph API
  • #853 switch-controller: unallocated ports parked in iotLocal; a verb to park a port; validate warns 2.3
  • #854 switch-controller: a LAG member follows its lead port 2.3
  • #860 switch-controller: read the switches after every sweep, so switch-ports is not judged on a stale read 2.3
  • #856 admin VPN: the peer config carries the site's DNS and routes to the DMZ gateway 2.3
  • #875 IPv6 egress: a health gate, and re-learning the WAN router after an ISP change 2.3
  • #668 A machine's zone is recorded, not enforced
  • #697 Node lan trunks and their switch ports are set once at install, maintained by nothing
  • #706 A wireless device zone cannot be checked against its SSID
  • #731 Controller-architecture modules never register themselves, and inventory drift has no routine check 2.3
  • #155 Modules for physical devices
  • #158 Multi-NIC module firewall rules
  • #236 RADIUS MAC Auth Bypass: a device's VLAN from its MAC, on Wi-Fi and wired ports
  • #63 Service desk tooling spike
  • #861 Moving a 2.2 site to main: four bugs and the workarounds used 2.3
  • #862 A VLAN tag or subnet already on the firewall is not detected when a zone is authored: template wan (VLAN 100, 10.1.0.0/24), zones init/merge, and add 2.3
  • #843 Create a SIEM for TAPPaaS
  • #851 CrowdSec on the firewall: rate limiting and bans for published names 2.3

G6.2 User interface & physical view

A general TAPPaaS user experience, starting with a physical view of the site: nodes, bonds, switches and ports, APs, WAN and satellite, with zones, guests and live state on them (#839; homelable as a starting point), designed first in a spike and an ADR (#844). The service catalog (#183) and Backstage (#91) are decided against it. Linked to G6.1.

  • #844 Spike and ADR: design the TAPPaaS user interface 2.3
  • #855 console: a destructive verb's typed confirmation names the object, not the switch 2.3
  • #839 TAPPaaS user interface: a general UX, starting with a physical view of the site
  • #183 TAPPaaS service catalog 2.3
  • #91 Backstage as a component of tappaas-cicd 2.3

G6.3 Data model

The meta model of the configuration is written down in src/DATAMODEL.md (2026-10-22: every JSON, which field names which object, and seven places where the model says one thing twice). The operator decided to fix the first four and park the rest (#866). Where a thing is becomes two things, a location and a placement, by ADR-036 (Accepted 2026-10-23). Linked to G6.2: the console's Physical view draws what this group models.

  • #865 The zone names its environment; environment.network.zone and zone.serves[] go 2.3
  • #867 Drop site.organizations: the identities/organizations directory is the list 2.3
  • #869 ADR-036 step 1: site-manager owns locations, with a primary one; a backup buddy's place is a location 2.3
  • #870 ADR-036 step 2: position-manager (was rack-manager), placements in rooms in a location 2.3
  • #871 ADR-036 step 3: every device record names its location 2.3
  • #872 ADR-036 step 4: the nodes are pvehost machines; site.hardware.nodes goes 2.3
  • #873 ADR-036 step 5: the console draws locations, rooms, racks 2.3
  • #868 Faceplate catalog: a site adds plates for models the catalog lacks 2.3
  • #876 A Service zone may serve several environments (shared service zone) 2.3
  • #866 Data model: parked simplifications (backup cascade, module placement, module:service)
Wave 5

Live verification

Tests, proofs and sign-offs on real systems, kept apart from development and bug fixes: what is built and waits for a real VPS, a real buddy or a real failure, and the test conventions themselves.

2 groups 11 open 1 done for 2.3
Groups and issues

G5.1 Satellite & off-site

Everything the satellite and the off-site copy still have to prove: the satellite as a module on a real VPS (#671) and makerfloss's satellite converted (#775), the site-liveness watchers in a real outage (#776), the buddy tunnel and relay (#796, #795), the sshPort passthrough (#762) and the ADR-010 hardening sign-off (#406).

  • #671 Deep test: satellite as a module on a real VPS
  • #775 Convert makerfloss's satellite1 into a module instance 2.3
  • #776 Report a site that has gone dark (outside heartbeat)
  • #796 backup: a buddy's WireGuard tunnel set up with the peer (peer add … --tunnel)
  • #795 satellite: relay a buddy's WireGuard tunnel to the home firewall
  • #406 ADR-010 P7 hardening + sign-off
  • #762 satellite: build the sshPort SSH passthrough to tappaas-cicd (ADR-010 §4.4.2)

G5.2 Test coverage & conventions

How tests run (a test verb on the managers, #499) and the module tests that still miss their conventions or probe the wrong thing (#119, #120, #409, #658).

  • #499 test verbs on managers
  • #658 nextcloud test 12 probes the browser URL from wherever it runs
  • #120 litellm test.sh conventions
  • #119 openwebui test coverage
  • #409 vllm-amd: test-model.sh drifted from the compose update.sh generates
Wave 4

Additive capabilities

New capabilities with low upgrade risk.

5 groups 3 open 18 done for 2.3
Groups and issues

✓ G4.1 Alerting & cluster resilience

Be told when something breaks, and survive more of it. Built: alerts from Loki by mail, every VM's logs in Loki, a credential per pushing VM, container severities from the line, each line's own timestamp, a release CVE preview with majors gated at the boundary (#788); two-node quorum with a QDevice verb (#835), HA failover only to nodes that carry the guest's bridges (#836), a site-timezone gate (#837). A site gone dark is now watched from outside (#776, built — its proof is in G5.1); the bonded lan uplink (#40) and the mail relay (#125) moved to G6.1.

  • #590 Second corosync link 2.3
  • #835 Two-node clusters self-fence: warn in node add, support a QDevice 2.3
  • #836 HA placement only on nodes that have every bridge the VM uses (firewall WAN) 2.3
  • #837 Health check: site.json location and timezone vs the nodes and guests 2.3

✓ G4.2 Proxy & ingress

Publish less of each app: limit a route to some paths (#642), some zones (#643) or a provider's source addresses (#656), as ADR-023 describes. One model for how a module's routes are authenticated — several ports, native app clients, machine callers (#845, ADR-032). Decide whether an internal proxy is needed at all (#154).

  • #642 Limit a route to paths 2.3
  • #643 Per-route allowed zones 2.3
  • #656 Limit a route to a provider's source ranges 2.3
  • #845 ADR: how a module's services are exposed — several ports, native app clients, API access 2.3
  • #154 firewall:internal-proxy 2.3

✓ G4.3 Manager verb gaps

The verbs operators otherwise do by hand (the test verbs, #499, are in G5.2): install a whole stack (#763), suspend a module (#428), migrate by changing .node (#429), private repositories (#634), module-level --set rules (#654).

  • #763 module-manager add-stack: install a stack's modules in dependsOn order 2.3
  • #428 module-manager suspend 2.3
  • #429 Migrate by modifying .node 2.3
  • #634 Private repositories in repository add 2.3
  • #654 module-manager: the --set pre-gate ignores the module-level field declarations (ADR-020 D10) 2.3
  • #831 module-manager delete: an archived module still counts as a dependent 2.3
  • #857 Every update on main rolls back: suspend-module.sh committed without the executable bit 2.3
  • #852 modify --set zone0 fails its own pre-update tests 2.3
  • #829 backup-manager restore restore over a module's own guest fails without a terminal 2.3

G4.4 Storage & physical devices

Shared storage as a managed part of a site: the rest of the NFS service (#772). The physical network (#668, #697, #706, #731, #155, #158) moved to G6.1.

  • #772 ADR-029 NFS: remaining work packages from #766

G4.5 Governance, CI & sign-offs

The project's own machinery (CI runners, #415, moved to G7.1 with the Woodpecker module): community-health files (#486), the remaining ADRs (#359; the ADR-010 sign-off, #406, is in G5.1), a tool for urgent patches (#746), a logging-function lint (#747); the service-desk spike (#63) is in G7.2.

  • #486 ADR-015 community-health files
  • #359 legal/processor ADR
Wave 3

Stability & correctness

Low upgrade risk. Build continuously, in any order within a group.

5 groups 18 open 22 done for 2.3
Groups and issues

G3.1 cluster:vm lifecycle & capacity

Guests sized, placed, started and stopped right: memory defaults from measurement (#663), moving a running guest to another node (#825), a readiness wait that skips UDP ports (#826), hardware drift (#531), shutdown and boot order (#36, #127), storage growth and placement (#100, #403), a composite-module ADR (#761), and a sweep that survives a guest restarting itself (#753). Built from the 2.0→2.2 field report (#832): a capacity pre-flight before the sweep (#833) and visible size changes on legacy sites (#834). Next by design: dynamic placement on PVE 9.2's load balancer (#838).

  • #531 Detect hardware-spec drift 2.3
  • #827 HA failback can move a host-CPU guest live to a node with an older CPU 2.3
  • #100 Automatic storage extension 2.3
  • #403 Install on the root disk; smarter placement
  • #838 Dynamic placement of workloads (PVE 9.2 CRS load balancer)
  • #833 Sweep pre-flight: refuse an over-committed node; a minimum host size 2.3
  • #834 First converge on a legacy site applies config sizes over those tuned in Proxmox 2.3
  • #832 Field report: a 2.0 site upgraded to 2.2, then a second node 2.3
  • #663 Memory defaults are not based on measurement 2.3
  • #825 update-module: changing a running guest's node does not migrate it 2.3
  • #826 Readiness wait probes UDP ports over TCP — 180 s lost per update 2.3
  • #761 A composite-module ADR: cluster, kubernetes, and modules that only provide artefacts

G3.2 Identity & SSO wiring

Every app wired to the identity provider the same way: OIDC for hass, Nextcloud and forgejo (#345, #401, #282), a site-specific login page (#479), logging's SSO dependency (#701), a test for forward-auth (#752), and Jellyfin behind forward-auth (#805) with Authentik sign-in (#806).

  • #345 hass: appliance-aware OIDC staging
  • #401 Nextcloud: OIDC redirect, trusted domains 2.3
  • #479 Site-specific login page 2.3
  • #858 identity: tile icons and the site logo need /admin/file/, so a site below Authentik 2025.12 can never update 2.3
  • #701 logging depends on identity though its own code makes SSO optional 2.3

G3.3 App module fixes

The known rough edges of the app modules, fixed and tested so they stay fixed: Nextcloud Talk end to end (#715), Home Assistant (#552, #568, #571 — maintainer-owned), Nextcloud and euro-office (#332, #751; its test 12, #658, is in G5.2), litellm consumers (#553), windows-server (#411), forgejo (#283, #284), in-guest backups (#754), container stop timeouts (#786).

  • #552 hass: a lost LLAT blocks every update
  • #568 hass: backup freeze leaves Frigate unhealthy
  • #571 hassanova has no release baseline
  • #553 litellm:models cannot reach an appliance consumer
  • #715 Nextcloud-hub revisited: Talk never worked end to end; races, retries and tests hid real failures
  • #411 windows-server: deploy-instances.sh missing
  • #332 euro-office / Nextcloud install findings 2.3
  • #807 Immich: bump unstableSrc once jellyfin-ffmpeg fixes six upload-reachable CVEs 2.3
  • #850 Upgrade Jellyfin to 12.x and the SSO plugin to the maintained fork 2.3
  • #859 jellyfin: jellyfin-tappaas-policy fails the activation on first run 2.3
  • #751 nextcloud:fileservice has no teardown 2.3
  • #754 In-guest backup timers: drop for PBS or say why they stay

G3.4 AI stack maturity

A production-grade AI stack (its test conventions, #119 and #120, are in G5.2): hardened (#121) and kept current (#621), LiteLLM sized for its workers (#677), and vLLM's models recoverable after a restore (#758).

  • #121 litellm production-grade
  • #621 Bump litellm / openwebui
  • #810 openwebui: container runs as root on the host network, with server-side STT on 2.3
  • #811 CVE scan: native libraries bundled in Python wheels are not seen 2.3
  • #812 vllm-amd: privileged LXC, unauthenticated vLLM API, sshd password auth
  • #813 tappaas-cicd: Nix store fills the mothership disk between weekly GCs 2.3
  • #758 vllm-amd: the models bind mount is in no backup
  • #677 litellm's default workers do not fit its default 4 GB

G3.5 Installer UX

A first install that is easier to get through: resumable (#377), showing its progress (#266), asking only what the hardware needs (#405), reusing what it already downloaded (#83).

  • #377 Installer cannot restart; mgmt-net routing
  • #266 Progress indicator during the PVE update 2.3
  • #83 Reuse already-downloaded images
Wave 2

Network behaviour hardening

Changes to live firewall and DNS behaviour on every installation. Restrictive defaults get harder to introduce the longer users rely on permissive ones, so they come early; every change shows its dry-run diff (#645) before --apply.

2 groups 3 open 21 done for 2.3
Groups and issues

G2.1 Firewall exposure & rule order

Tighter firewall defaults that keep what modules need. Built: the gateway, GUI/SSH and anti-spoofing rules. Sprint from 2026-10-12: the review of the tier model and rule creation is done and published (#816), with most of its findings fixed; pinholes no longer shadowed (#386); every zone policy covers IPv6 (#815), and IPv6 itself is built (#26). The rules manager's overlap report, covered pinholes and sequence map are built (#160, #256, #162, on the branch). The review findings are done: #817, #818, #822 closed; #819–#821, #823 and the ADR amendments (#824) closed — on main 2026-10-13 after a clean --deep (144/0). Inbound IPv6 to Caddy, IPv6 anti-spoofing, DNSSEC (#263), #576, #257, #660 and #683 followed, on main 2026-10-13 after a clean --deep (144/0). Lockout check re-run on hrossen 2026-10-13 against the current rules, IPv4 and IPv6: only mgmt reaches the firewall's GUI and SSH, on any address; the WAN passes 80/443 and nothing else (the admin VPN not re-probed). Open: the NetBird rules below, pending the NetBird decision; #706 moved to G4.4 behind #668. NetBird rules (#385, #222) are low priority until the NetBird decision.

  • #816 Review the zone tiers and rule-set creation; publish how it works 2.3
  • #386 block-private rules shadow auto-pinholes 2.3
  • #815 IPv6 parity: zone policy covers IPv6 before any zone gets an address 2.3
  • #817 Egress to: internet also passes every private and zone network 2.3
  • #818 alias: peers skip the zone-policy check 2.3
  • #819 alias: ingress bound to lan; a failed interface lookup falls back silently 2.3
  • #820 A description change duplicates a module rule 2.3
  • #821 The pinhole check exists twice and the copies can disagree 2.3
  • #822 Block-private misses 100.64/10, 169.254/16 and the firewall's other addresses 2.3
  • #823 Gateway lookup is one unretried API call per zone 2.3
  • #824 Amend ADR-014 and ADR-021 to the zone and rule model as built 2.3
  • #576 Zone serves as string or array 2.3
  • #257 mDNS in zones.json 2.3
  • #160 Overlap detection in the rules manager 2.3
  • #256 Skip pinholes already covered by zone access 2.3
  • #162 Persistent sequence-map artifact 2.3
  • #660 A declared aliasType change cannot be applied 2.3
  • #683 Declared ports without network:rules are inert 2.3
  • #385 Persist the NetBird OPT1 pass rule
  • #222 NetBird → srv floating rule is fragile

G2.2 DNS resolver robustness

A resolver that holds up: a working Unbound restart (#387), a written IPv6 stance (#149), DNSSEC with split horizon (#263), and optional blocklists (#157). Public DNS that follows the WAN (#383, #830) moved to G6.1.

  • #387 Unbound rc.d restart fails (Python mismatch) 2.3
  • #149 IPv6 root lookups fail → do-ip6: no 2.3
  • #263 DNSSEC on Unbound 2.3
  • #157 Default DNS blocklists (DNSBL / maltrail)
Wave 1

The breaking-change window

Everything that renames, re-schemas or rebuilds, landed while few installations depend on today's shapes — each change with its migration and an upgrade test from the current stable, not only a fresh install.

3 groups 4 open
Groups and issues

✓ G1.2 Backup placement model + managed machines (ADR-012, ADR-026)

Backups where the operator chooses — a node, a managed machine, an external PBS or the satellite — encrypted, with every machine TAPPaaS manages a module (ADR-012, ADR-026). The satellite's live test (#671), makerfloss's satellite (#775) and the buddy tunnel and relay (#795, #796) moved to G5.1.

    G1.6 Secrets & privileged access

    Least privilege on the platform. Built: nodes hardened to the Proxmox guide, key-only SSH, encrypted backups. Next: how an AI agent may reach the mothership (#142), SSH to the mothership through the satellite (#762) moved to G5.1. Secrets management (#58) waits in Future Work.

    • #142 RFC: AI-agent access to cicd

    G1.7 Parked for Erik discussion

    Waiting on a conversation between Lars and @ErikDaniel007, not on work: the controller pattern (#430), hass locale (#348) and Erik's image pins (#765).

    • #430 Controller/manager pattern for app modules
    • #348 hass locale from site master data
    • #765 Container image pins: list litellm, openwebui, vllm-amd, deconz; move the stale ones
    10 Oct 2026

    TAPPaaS 2.2

    Every VM and container backup is now encrypted; the firewall admits its GUI and SSH only from management, gives other zones nothing but DNS, NTP and DHCP, and drops spoofed traffic; nodes are hardened to the Proxmox guide;

    v2.2 in staging
    What it carried, by group

    Tag v2.2, cut into staging 2026-10-10 by a release-train boundary.

    Safer by default, and you hear when something breaks. Every VM and container backup is now encrypted; the firewall admits its GUI and SSH only from management, gives other zones nothing but DNS, NTP and DHCP, and drops spoofed traffic; nodes are hardened to the Proxmox guide; Authentik is current again (2026.8), and a CVE scan runs after every update. Alerts reach you by mail — quorum loss, failing disks, out-of-memory kills, failed backups — with every VM's logs in Loki. Guests are built on the mothership, so updates work without internet, and restores of a node and of the mothership have been rehearsed. New: NFS shares from the nodes, Immich and Jellyfin (Testing), and forgejo with single sign-on in a DevOps stack. Upgrading: each guest's first backup afterwards is a full upload, and Authentik steps through its versions over several updates.

    The rows below are what it carried, by the wave and group they came from.

    G0.1 Migration & rollback framework

    Changes to config/ that every site can take safely: versioned migrations with backup, dry-run and a ledger (the contract is ADR-025), rollback in install and update, and one meaning for --force.

    • #736 update merge pins a field that a release moves to a new place

    G1.1 Vocabulary & classification (ADR-022 family)

    Settle the words before they spread into schemas, CLI names and config/ paths: the ADR-022 vocabulary — kind, scope derived from stack, People → Identity — everywhere. Left for a later release: removing the tier fallback.

    • #676 tier → scope migration (ADR-022e)
    • #705 cluster and templates carry a kind ADR-022d denies them
    • #741 update-tappaas sweeps a Proxmox VM template as if it were a module

    G1.2 Backup placement model + managed machines (ADR-012, ADR-026)

    • #756 debianhost: key-only SSH as an explicit step
    • #760 VM and container backups are not encrypted, against ADR-012
    • #737 site.json backup: true fails validation and has no migration
    • #742 backup:filesystem update never creates a missing encryption key, so a capture can fail forever
    • #745 A dangling symlink in a declared path refuses the whole capture
    • #407 ADR-012 3-node live validation gate
    • #749 Installing backup on a PVE node silently removed its PVE apt repository

    G1.3 Module contract & repo layout

    One module contract — what community modules copy and deployed configs point at: version and status rules, one catalogue, the blueprint check at every release, modules grouped by stack, and a tool to move them (MOVE-MODULE.md).

    • #421 Restructure src/apps into stacks
    • #674 Implement the ADR-027 blueprint check

    G1.4 Common NixOS baseline

    One NixOS baseline every VM imports, carrying the site's time zone, locale, keyboard and time source — the same facts applied to Debian guests and nodes — and pins with a cadence. Left: hass locale (#348, G1.7) and Windows guests (#678, Future Work).

    • #324 App VMs do not import tappaas-common.nix
    • #740 update-os.sh ships only *.nix siblings, so a module cannot carry a patch or data file
    • #748 identity: the Authentik pin has not moved since 2026-02-21, and nothing tracks it
    • #750 Container image pins have no cadence and no tripwire, unlike the nixpkgs pin
    • #777 Release train: lock nixos-unstable beside the pin, so Immich 3 follows release moves

    G1.5 Rebuild & recovery paths

    A proven way back before anything breaking: config/ backed up and restorable, mothership keys that can be reissued, restores of a module, a node and the mothership rehearsed, and a control plane that checks itself and rolls back.

    • #755 cicd-key.sh ignores kind: machine modules — rotate locks the mothership out of satellite and debianhost
    • #729 Rehearse RESTORE.md §2, §3 and §5.2 on the test system
    • #314 PVE 9.2 default + upgrade path
    • #417 node delete does not remove the node
    • #711 templates:nixos re-apply fails with no cause and rolls the VM back
    • #720 update-os.sh hangs indefinitely when the rebuild's ssh session dies
    • #700 The pre-update gate tests guests that are still booting
    • #767 The sweep's systemd unit is not the operator's console — align it (ADR-017 D3)
    • #768 module update: a failed dependency (the OS rebuild) should stop the update, not run update.sh first
    • #744 A mothership release move ends the sweep with no record, no re-arm and no notice

    G1.6 Secrets & privileged access

    • #128 Security hardening (Proxmox hardening guide)

    G2.1 Firewall exposure & rule order

    • #399 Zone → gateway rule allows every port
    • #384 Restrict webgui 8443 to mgmt + NetBird
    • #159 Anti-spoofing + drop fragments, always on
    • #375 mgmt.access-to: "all" sentinel

    G3.1 cluster:vm lifecycle & capacity

    • #392 Never attempt a disk shrink
    • #393 A failed migration must not block later steps
    • #532 Pending vs applied hardware changes
    • #36 VM shutdown timeout
    • #127 Boot order: firewall (and secrets) first
    • #37 Optimise RAM (ballooning, swap)
    • #423 Create-TAPPaaS-LXC.sh has no debug()
    • #753 A guest restarting during its own update fails the sweep
    • #793 A live migration leaves a FreeBSD guest's agent dead (firewall)
    • #773 cluster:vm thinDisk — opt-in thin provisioning per module
    • #764 The Proxmox VM summary note links "Discussions": make the links docs, code, issues

    G3.2 Identity & SSO wiring

    • #282 forgejo SSO (Community repo)
    • #738 identity:identity discovery check fails on a changed host key and blames the firewall
    • #797 identity: an update skips Authentik release lines when the guest is built on the mothership
    • #752 identity:accessControl has no test-service
    • #805 jellyfin: put the published instance behind Authentik forward-auth
    • #806 jellyfin: sign in with Authentik (SSO-Auth plugin behind forward-auth)
    • #794 The groups claim mapping uses User.ak_groups, deprecated since 2026.2
    • #769 vaultwarden: SSO is registered but never wired into the app
    • #779 identity: authentik-env-secrets-only calls cmp without diffutils

    G3.3 App module fixes

    • #51 Immich
    • #808 euro-office: test.sh fails a healthy DocumentServer, 50 s too short
    • #283 forgejo central logging (moved to src/devops, 2026-10-04)
    • #786 identity, litellm: containers get SIGKILL on every stop (10 s timeout)
    • #743 nextcloud:fileservice test contradicts euro-office UPGRADE.md

    G3.5 Installer UX

    • #405 Do not ask for tanks without free disks

    G4.1 Alerting & cluster resilience

    • #126 Alerting from Loki through the #651 channel (was: no alerts for quorum loss / node dropout)
    • #165 logging v2: Loki auth, Grafana OIDC
    • #788 Release train: flag a major version change at a pin move
    • #770 App VMs do not ship their logs to Loki
    • #784 Loki stores receive time — backfill and reboot entries collapse
    • #785 Container stderr is labelled severity=error regardless of level
    • #774 Build guest systems on tappaas-cicd — guests neither evaluate, compile nor download

    G4.2 Proxy & ingress

    • #778 network:proxy publishes names with no public DNS record: failing ACME, dead SSO tiles
    • #809 network:proxy update keeps the old domain after a proxyDomain change

    G4.3 Manager verb gaps

    • #759 backup-manager: no verb to take a module's backup now
    • #739 After reinstalling a provider, dependsOn consumers stay unwired
    • #789 backup-manager: no verb to delete a module's restore points
    • #790 environment-manager delete leaves the env's DNS override, cert and refid
    • #791 network-manager delete leaves the zone in other zones' lists

    G4.4 Storage & physical devices

    • #388 cluster:storage (NFS first)
    • #766 Implement ADR-029: NFS service (pvehost:nfs)
    • #792 unifi.sh: whole API payloads as jq arguments fail on large sites

    G4.5 Governance, CI & sign-offs

    • #143 CVE tracking / SBOM (ADR-011)
    • #746 Emergency patching as ADR-028 D12 describes it
    • #747 A script that defines its own logging set must define all of it, debug included
    25 Sep 2026

    TAPPaaS 2.1

    Wave 0 complete — a versioned config-migration runner with backup, dry-run and a ledger (ADR-025); honest pre-update gates; scheduled updates on a channel with an email when a sweep fails (ADR-017); a 3-way merge that can no longer skip a module silently.

    v2.1
    What it carried, by group

    Tag v2.1 (83ccb49837d1), the first release-train boundary (forced), numbered retroactively on 2026-10-02 when versions were introduced. Estate pin: nixpkgs c508844df6c2 (nixos-26.05). 718 commits after 2.0. Much of it is already in production: stable moved on to d796e254 (2.0+466) on 2026-09-18, before versions existed.

    What it delivered: Wave 0 complete — a versioned config-migration runner with backup, dry-run and a ledger (ADR-025); honest pre-update gates; scheduled updates on a channel with an email when a sweep fails (ADR-017); a 3-way merge that can no longer skip a module silently. Most of Wave 1 — the ADR-022 vocabulary (kind, People → Identity); managed machines as modules (ADR-026: debianhost, module adopt, pvehost) and the backup placement model (ADR-012 v1.0); the module contract (moduleSource, the module catalogue, version/status claims, move-module.sh); site-wide time zone, locale, keyboard and time source on every guest; the move to NixOS 26.05, staged for the next boot; mothership keys that can be reissued and revoked, and key-only SSH on the nodes. Plus a first set of Wave 2–4 fixes.

    The rows below are what it carried, by the wave and group they came from.

    G0.1 Migration & rollback framework

    • #652 Versioned config-migration step
    • #584 Rollback in install/modify
    • #453 --force overwrites deployed config
    • #655 module-manager update verb
    • #648 --unset for stale fields
    • #572 repo-sync auto-stash never restored
    • #727 module-manager: --allow-disruption reaches services that reject it

    G0.2 Trustworthy gates

    Tests that tell the truth, so every later migration can be verified.

    • #644 network-manager <verb> --help runs the verb
    • #635 Pre-update gate collapses test severity
    • #633 site-manager update --force overrides every rebootOk
    • #636 backup:vm Check 1 fatal while a backup runs
    • #555 network:proxy checks cannot fail without a refid
    • #560 identity:identity never checks the consumer side
    • #620 Tier lattice authored on 9 of 26 zones
    • #645 Reconcile dry-run hides firewall rule changes
    • #689 Service tests cannot tell an absent pinhole from one never requested

    G0.3 Update channel & failure notice

    A scheduled update on a channel, with a mail when a sweep fails — the path every later fix takes to a site.

    • #471 ADR-017 update scheduling
    • #447 site-manager cannot modify the schedule
    • #651 A failed sweep notifies no one
    • #357 Define updateWindow / updateChannel
    • #653 Hold the scheduled pull on one site

    G0.4 Merge trust

    A 3-way merge that can no longer skip a module silently, so Wave 1's re-schemas reach every deployed config.

    • #659 Step 0 skipped when a config has no .location, update still reports success
    • #688 The 3-way merge reports adopting fields it does not write

    G1.1 Vocabulary & classification (ADR-022 family)

    • #624 ADR-022 review comments
    • #637 ADR-022 comments (second pass)
    • #610 Site = administrative domain × location × zone
    • #611 kind values; retire external-host (ADR-022d/f)
    • #599 Glossary: node vs host vs cluster member
    • #628 People → Identity (people-manager → identity-manager)
    • #422 Glossary rewrite
    • #707 ADR-014: zone.tier has no stated relation to IEC 62443 levels
    • #708 ADR-004: stack is defined three times, with three value sets
    • #669 module-manager list and other verbs should handle Kind

    G1.2 Backup placement model + managed machines (ADR-012, ADR-026)

    • #780 debianhost module + module-manager module adopt
    • #781 ADR-012 topology §1.3 verified
    • #782 instance vs module name
    • #665 Register cluster nodes as machine modules
    • #670 Satellite as a module: retire satellite-manager
    • #783 placementState revisit
    • #691 backup:filesystem ignores its schedule; every capture fires at once
    • #435 backup: pass the required module argument in the deep PBS check
    • #712 Retire the satellite's --os nixos path (unpinned, untested)
    • #602 Empty placementState resolves onto the wrong host
    • #600 PBS on a non-cluster host
    • #612 vmname → the instance name, as a DNS alias; shim stays
    • #601 Placement discovery is PVE-only
    • #456 external placement for a pre-existing PBS
    • #607 Exit from external placement
    • #457 pbs_node uses placement, not the registered name
    • #603 PBS on a non-PVE host has no update path
    • #554 Reconcile creates duplicate job coverage
    • #609 Off-site location recorded
    • #605 Split ADR-012 acceptance list

    G1.3 Module contract & repo layout

    • #500 Automate moving a module
    • #349 Drop the zone tag from released modules
    • #566 Legacy name → variant convention
    • #248 Module version/status standard
    • #363 Module lifecycle blueprint ADR
    • #463 module-catalog schema is stale

    G1.4 Common NixOS baseline

    • #390 00-Template fails canon C4/C7
    • #448 NIC rename race (kernel / udev / cloud-init)
    • #472 NixOS clock two hours off
    • #408 Locale/keyboard: tappaas1 is the master
    • #87 NTP on OPNsense, consumed by modules
    • #716 A guest can boot into a 17–34 min window with no usable time source
    • #220 Nix sandbox disabled on cicd
    • #680 The baseline flake.lock decides every site's nixpkgs
    • #699 Guests created with -localtime 1 boot two hours ahead
    • #721 promtail is removed in nixos-26.05
    • #722 Grafana's secret_key has no default in 26.05
    • #726 A release move leaves every Postgres guest permanently warning about collation
    • #728 update-os: guests switch across a nixpkgs release and fail on exit 4
    • #709 Nextcloud cannot advance past 33

    G1.5 Rebuild & recovery paths

    • #545 Foundation backup + tested recovery (incl. config/)
    • #122 Reissue and revoke tappaas-cicd SSH keys
    • #43 Test and document backup/restore
    • #710 templates:nixos re-apply fails with no cause and rolls the VM back
    • #713 The mothership cannot undo a bad rebuild
    • #725 A switch across a nixpkgs release reports failure and its rollback live-locks
    • #719 The control-plane self-check does not verify what it claims

    G1.6 Secrets & privileged access

    • #19 Disable SSH password login on PVE nodes
    • #378 Default-on secret scanning on cicd
    • #724 The journal secret-scrub never worked (it redacted the key, not the secret)

    G2.1 Firewall exposure & rule order

    • #682 rules_manager.py reads the provider from location, not moduleSource
    • #684 test-module.sh fills its dependency set from dependsOn alone
    • #704 network:proxy: aliasType and firewallType warn as orphans

    G2.2 DNS resolver robustness

    • #693 An unpublishable name returns rc=0 where D5 expects 3
    • #657 Standard 4 invents an FQDN when a config lookup fails
    • #702 dns-manager add matches an entry by description, not by name
    • #703 acme-setup: wildcard DNS is skipped when only the apex is unpublished

    G3.1 cluster:vm lifecycle & capacity

    • #665 Register cluster nodes as kind: machine modules
    • #662 PVE host configuration is not backed up
    • #692 A guest is built on tanka1 whatever node it lands on
    • #686 A backup lock fails an update that succeeded
    • #679 An immutable field's schema default can make a module unreconcilable
    • #569 Node capacity / overcommit verb
    • #673 Cluster nodes are a fixed list of nine, not a sequence

    G3.2 Identity & SSO wiring

    • #698 logging update fails on makerfloss: the mgmt environment has no domain

    G3.3 App module fixes

    • #687 OnlyOffice: the document server cannot download from Nextcloud
    • #412 coturn reads an undefined publicDomain
    • #622 deconz probe uses a name that never resolves
    • #714 nextcloud:fileservice fails euro-office on a verdict the run could not make
    • #667 nextcloud:fileservice declares no fields, so its connector is undefined

    G3.4 AI stack maturity

    • #690 litellm VK provisioning has no readiness wait and discards the key

    G4.2 Proxy & ingress

    • #696 A blocked client gets a blank page: access lists carry no message

    G4.3 Manager verb gaps

    • #681 Auto-stash entries nothing lists or resolves
    • #672 module delete of a machine leaves its dnsmasq Host entry behind
    • #730 health-manager: no reboot verb, and pending reboots go unrecorded
    • #718 module-manager: the dependency test misses an environment's provider

    G4.4 Storage & physical devices

    • #734 ap-controller: an unfilled placeholder SSID reads as live drift
    • #733 unifi.sh: one message covers four failure causes, and a jq crash

    G4.5 Governance, CI & sign-offs

    • #695 The deep VM suite leaves a guest behind, and the next run fails on it
    • #694 Service README field sections are stale
    21 Jul 2026

    TAPPaaS 2.0

    The ADR-007 taxonomy, drafted during 1.x, implemented: one Site, classified by People, Apps and Environments, with health as a cross-cutting concern.

    v2.0
    What it delivered

    Tag v2.0 (61e91f080e01), tagged and released straight to stable before the release train existed; 259 commits after 1.1. The forge milestones Release 2.0: Build out Stacks (33 closed) and Release 2.0 Documentation (3 closed) tracked it; the rest of their issues moved into the waves above.

    One model and a control plane. The ADR-007 taxonomy, drafted during 1.x, implemented: one Site, classified by People, Apps and Environments, with health as a cross-cutting concern. The tappaas-cicd mothership gained the managers that run a site — module-manager, network-manager, people-manager (now identity-manager), environment-manager, site-manager, health-manager, backup-manager, satellite-manager — and the controllers behind them: opnsense-controller, proxmox-controller, identity-controller, backup-controller, switch-controller, ap-controller and the node-provisioner.

    Platform. The firewall became the network foundation module (OPNsense zones, DNS, reverse proxy); the optional satellite VPS for public ingress, off-site backup and an admin VPN (ADR-010, with a Debian option); new nodes installed over PXE (site-manager node add); an unattended install; and an operator runbook to migrate a 1.x site onto the new model.

    Website. tappaas.org rebuilt and served from Codeberg (cutover 2026-07-21, the day of the release).

    New ADRs: ADR-003 (dependency management), ADR-012 (backup enhancement), ADR-013 (documentation structure); the ADR-007 family implemented and reconciled with the as-built code.

    12 Jul 2026

    TAPPaaS 1.1

    The collaboration hub — nextcloud, euro-office, coturn and nextcloud-hpb for Talk — the deconz Zigbee gateway, Home Assistant as a sealed appliance (hass), and windows-server;

    v1.1
    What it delivered

    Tag v1.1 (889c61a2c848), the last 1.x release; 442 commits after 1.0.

    Stacks fill out. The collaboration hub — nextcloud, euro-office, coturn and nextcloud-hpb for Talk — the deconz Zigbee gateway, Home Assistant as a sealed appliance (hass), and windows-server; LiteLLM credential management, vLLM model tooling and Open WebUI 0.10. Foundation: central logging; Authentik-based identity with users, groups, roles and OIDC for modules (ADR-006); certificates through ACME.

    Design for 2.0. The ADR series that 2.0 implemented: ADR-004 (module catalogue and config cascade), ADR-005 (variants), ADR-006 (identity), the ADR-007 taxonomy family (007, 007a–f), ADR-008 (switches and APs), ADR-009 (composition meta-model), ADR-010 (satellite), ADR-011 (SBOM).

    13 Apr 2026

    TAPPaaS 1.0.0

    The first release: an automated installation of a self-hosted platform for a home or a small business, running on real systems — Proxmox nodes, an OPNsense firewall, a NixOS template, the tappaas-cicd mothership, backup with Proxmox Backup Server and Authentik identity as the foundation; on top of it the first app modules: Home Assistant, LiteLLM, Open WebUI, vLLM on AMD, Vaultwarden, the NetBird client and a UniFi controller.

    v1.0.0
    What it delivered

    Tag v1.0.0 (682b87ce3087), the first production-ready release of TAPPaaS; 673 commits from the first one (2025-05-10).

    The first release: an automated installation of a self-hosted platform for a home or a small business, running on real systems — Proxmox nodes, an OPNsense firewall, a NixOS template, the tappaas-cicd mothership, backup with Proxmox Backup Server and Authentik identity as the foundation; on top of it the first app modules: Home Assistant, LiteLLM, Open WebUI, vLLM on AMD, Vaultwarden, the NetBird client and a UniFi controller. Zones are VLANs on trunk ports (ADR-001, ADR-002).

    How we plan

    • The order comes from one plan, ROADMAP.md in the source repository: every open issue in a wave and a group, why they come in that order, and what each release carried. This page is built from it.
    • Which version reached which channel, and when, is in RELEASES.md.
    • Decisions are written down first, as ADRs in the source repository.
    • The details live on Codeberg: milestones and issues. To influence the direction, see Contributing.