Skip to content

Provisioning build order: ADRs to Ansible roles

Do not write provisioning logic solo. The decisions are made. These notes say which role owns which decision, and the safe order to build and run them.

Baseline: ADR-0021 (host), ADR-0022 (edge, TLS, hostnames), ADR-0023 (container isolation), ADR-0024 (Ansible layout), ADR-0025 (control-plane co-location), ADR-0026 (backups), ADR-0027 (secrets and credential custody), ADR-0031 (Tailscale admin access). (ADR-0021 to ADR-0027 superseded ADR-0014, which bundled all of it into one document.)

Role ownership

Role Owns ADR
common Base packages, timezone, unattended OS upgrades ADR-0021
hardening SSH (keys, no root, per-person users, non-standard port), fail2ban, firewall ADR-0021, ADR-0031
tailscale Joining the tailnet, so administrative access exists before public SSH closes ADR-0031
docker Docker and Compose, the network model (per-tenant, shared-core, isolated control plane), restart policy, resource limits, pinned image versions ADR-0023, ADR-0025
caddy Caddy install, wildcard TLS via the Cloudflare DNS challenge, hostname routing, /insights and /health subpaths ADR-0022
core_stack LangGraph brain, registry (Postgres, own network, tenants reach it only via the API), Console, monitoring ADR-0025, ADR-0030
tenant_stack One tenant: business Postgres (pgvector, pinned, RLS-ready), a dedicated n8n instance (Projects not used), n8n's own Postgres, private network ADR-0023
metabase Conditional. Only for tenants whose services list includes it. Pinned version plus a scheduled bump ADR-0023
dns Tenant DNS record creation via a zone-scoped Cloudflare token. Deletion is never automated ADR-0022, ADR-0024
backup Tiered by recoverability, per-tenant dumps to per-tenant paths, streamed through age to AU object storage ADR-0026
(secrets) Ansible Vault, per-tenant --vault-id, inline encrypt_string, no key material on the host ADR-0027

Safe build and run order

  1. hardening phase 1. Lock the box down, but leave public SSH open. Verify both admins.
  2. tailscale phase 2. Join the tailnet, disable key expiry, verify both admins over Tailscale.
  3. hardening phase 3. Close public SSH. Admin access is now Tailscale only.
  4. docker. Runtime and networks.
  5. caddy. Front door and TLS. Needs DNS plus the scoped Cloudflare token. Milestone: https://console.octopodia.com.au serves a placeholder. That single page proves DNS, firewall, Caddy, the token and TLS all work.
  6. core_stack. Brain, registry, Console.
  7. tenant_stack. Stand up client-zero as tenant zero.
  8. backup. Before any real data matters. The restore test is the deliverable, not the dump.
  9. (secrets) Threaded through all of the above. Vault from the start, never bolted on.

Steps 6 and 7 are blocked. There is no brain or registry image to deploy yet. Steps 1 to 5 are buildable and testable today. Stop at the milestone in step 5.

Client-zero: tenant zero, not client one

One tenant: Octopodia's own inbox. Email triage only. Assist autonomy, so every draft is approved. Prove the full stack on ourselves for roughly a month before the first paying client.

Greenhills is the first client tenant, not tenant one. We do not learn tenant provisioning on a paying client. ADR-0025's "shared by default" applies to both, and the ordering is client-zero first.

Deferred: hooks exist, logic later

Block-storage migration, RLS enforcement (ADR-0017), tenant mode and expiry logic (ADR-0013), the reconciliation pass (ADR-0006), and the control-plane split (ADR-0025 ยง3).