diff --git a/Architecture.md b/Architecture.md
index 6c6946f..4bf341f 100644
--- a/Architecture.md
+++ b/Architecture.md
@@ -1,143 +1,180 @@
-# Architecture
+# Atlas OS — System Architecture
-Atlas OS runs on **one box**. Not a cluster, not a hyperscaler region, not a sprawl of managed services — a single EU-sovereign Hetzner VPS (NL/DE) that hosts the entire stack. This page explains why that is a deliberate design choice, how the edge funnels every request through one identity gate, and how internal services stay invisible to the public internet.
+## Overview: The One-Box Philosophy
-> EU-sovereign by design: hosting sits in NL/DE, outside the reach of the US Cloud Act.
+Atlas OS is deliberately a single-server architecture. One Hetzner CPX32 VPS
+(8 vCPU · 16 GB RAM · 150 GB NVMe · Nuremberg) runs the complete operational
+infrastructure of Atlas Corporation: CRM, finance, communications, AI, document
+management, fleet control, and 11 websites.
+
+**Why one box?** For a solo founder, operational simplicity outweighs redundancy.
+One server = one bill, one SSH target, one Caddy config, one Authentik instance,
+and zero inter-datacenter latency. The Tailscale overlay provides device-level
+redundancy for access even if Caddy is down.
---
-## The one-box philosophy
+## Network Topology
-Everything Atlas needs to operate as a business lives on a single host:
-
-- **~55 Docker containers** running the full application estate
-- **~24 public services** exposed to the internet
-- **One Caddy edge** terminating TLS and routing every hostname
-- **One identity provider** (Authentik) gating access before any app is reached
-
-The advantage is operational gravity: one place to back up, one place to patch, one network namespace to reason about, one TLS configuration, one SSO policy. A solo operator can hold the whole system in their head. The trade-off — a single failure domain — is mitigated by nightly backups and the discipline of keeping the surface small.
-
-| Layer | Component | Role |
-|---|---|---|
-| Edge | Caddy | TLS termination, reverse proxy, `forward_auth` to SSO |
-| Identity | Authentik (embedded outpost) | SSO gate in front of protected apps |
-| Apps | ~55 Docker containers | Cockpit, Work, AI, Files & Comms, Ops, Sites |
-| Private spine | Tailscale | Internal-only reach via `tailscale serve` |
-| Mesh | NATS | Node-to-node messaging |
-| Remote | MeshCentral | Manage client POS / devices |
-
-Admin identity across the box: **atlasshb**.
-
----
-
-## Request path: Caddy edge → Authentik SSO → apps
-
-Every public hostname resolves to Caddy. Caddy terminates TLS, then — for protected routes — performs a `forward_auth` handshake against the **embedded Authentik outpost**. Only after Authentik confirms the session does the request reach the upstream container.
-
-```mermaid
-%%{init:{'theme':'base','themeVariables':{'primaryColor':'#0c1428','primaryTextColor':'#cfe2ff','primaryBorderColor':'#1F6FE0','lineColor':'#4EA0FF'}}}%%
-flowchart TD
- U[Browser / client] -->|HTTPS| C[Caddy edge
TLS termination]
- C -->|forward_auth| A[Authentik
embedded outpost]
- A -->|session valid| C
- A -.->|no session| L[Login / MFA]
- L --> A
- C -->|authed request| OS[Atlas OS desktop
command.atlascorporation.nl/os/]
- C --> APPS[Protected apps]
-
- subgraph box["One Hetzner VPS — EU-sovereign · ~55 containers"]
- C
- A
- OS
- APPS
- subgraph internal["Internal-only (no public ingress)"]
- T[Loopback-bound services]
- TS[tailscale serve]
- end
- APPS --- T
- T --- TS
- end
-
- TS -.->|private spine| TN[Tailscale tailnet]
- APPS --> M[(The Mind
kernel.sqlite spine)]
- M --> R[LLM router :8888]
+```
+ ┌──────────────────────────────────────────────┐
+ Public Internet │ atlas-01 (Hetzner CPX32) │
+ ───────────── │ ┌─────────┐ ┌──────────┐ │
+ Browser/App ──────►│ │ Caddy │───►│Authentik │──► Services │
+ │ │ (TLS) │ │ (SSO) │ │
+ │ └─────────┘ └──────────┘ │
+ │ │
+ Tailscale mesh │ ┌────────────────────────────────────────┐ │
+ ──────────────── │ │ Private tailnet (100.x.x.x) │ │
+ iOS / PC / Laptop─►│ │ Webtop desktop · internal dashboards │ │
+ │ └────────────────────────────────────────┘ │
+ └──────────────────────────────────────────────┘
```
-The browser desktop at **command.atlascorporation.nl/os/** is itself one of these protected apps — 39 apps organised into Cockpit, Identity, AI, Work, Files & Comms, Ops, and Sites categories. See [[Atlas-OS]] for the full app catalog and [[Security]] for the SSO and MFA model.
+### Public → SSO path (every service except mail ports)
+1. Request hits Caddy (443 → Docker port)
+2. Caddy runs `forward_auth` against Authentik outpost
+3. Authentik validates session cookie / issues redirect to login
+4. On success: Caddy copies `X-Authentik-*` headers and proxies to service
+
+### Tailscale-only services
+- Webtop remote desktop (`tailscale serve :8497`)
+- Internal dashboards (portal, status feeds)
+- atlas-01 SSH (blocked on public firewall, tailnet-only)
---
-## Loopback-binding + `tailscale serve` for internal services
+## Service Layers
-Not everything should be reachable from the public internet. Atlas keeps its internal surface small with two complementary techniques:
+### Tier 0 — Identity & Security
+- **Authentik** (`auth.{DOMAIN}`) — OIDC/SSO, MFA, app proxies
+- **Vaultwarden** (`vault.{DOMAIN}`) — end-to-end encrypted password vault
+- **Caddy** — automatic TLS, reverse proxy, the sole public entry point
-### 1. Loopback binding
+### Tier 1 — Core Business
+- **Odoo 19** — CRM, invoicing, project management, contacts
+- **Nextcloud 31** — file storage, DAV (calendar/contacts sync to iOS)
+- **Stalwart** — self-hosted email: SMTP, IMAPS, JMAP, Sieve
+- **Forgejo** — self-hosted git (this very repo)
-Internal-only services bind to **loopback** rather than a public interface. They are reachable from the box itself (and from other containers/proxies on the host) but have **no public ingress**. Caddy can still reverse-proxy to them when an authenticated, SSO-gated route legitimately needs them — but the open internet cannot hit them directly.
+### Tier 2 — Finance & Operations
+- **Firefly III** — double-entry bookkeeping, budgets, reconciliation
+- **Enable Banking** — PSD2 bank feed (ING, Knab → Firefly)
+- **Paperless-ngx** — document management, OCR, auto-tagging
+- **n8n** — workflow automation (webhooks, scheduled tasks)
+- **Uptime Kuma** — service health monitoring with alerting
-### 2. `tailscale serve` over the private spine
+### Tier 3 — AI & Intelligence
+- **LiteLLM proxy** — unified OpenAI-compat API to all models (local + cloud)
+- **Open-WebUI** — ChatGPT-style UI on top of LiteLLM
+- **Atlas Brain** (`atlas_brain.py`) — Python signal collector, runs every 15 min
+- **Atlas Loop** — background nudge cycle, publishes to NATS
+- **Atlas Meridian** — autonomous decision engine / battle responder
+- **Langfuse** — LLM observability: every call traced with latency, cost, tokens
+- **Local model router** (`:8888`) — OpenAI-compat router for local Ollama models
-Where a service needs to be reached from another machine — but should never be public — it is exposed via **`tailscale serve`** on the Tailscale tailnet. This is the private spine: only devices enrolled in the tailnet can reach those services. The rule is explicit and absolute:
+### Tier 4 — Fleet & Infrastructure
+- **MeshCentral** — remote device access, POS terminal fleet management
+- **NATS** — real-time pub/sub event bus across all Tailscale devices
+- **Atlas heartbeat sink** — aggregates device heartbeats from the NATS mesh
+- **Atlas cockpit feeds** — on-disk JSON feeds consumed by the OS desktop
+- **Webtop** — full XFCE Linux desktop in the browser (tailnet-only)
-> Internal services are reached via `tailscale serve` — **never** exposed publicly.
+### Tier 5 — Managed Sites (11 sites)
-This is why the public service count (~24) is far smaller than the container count (~55): the majority of containers are either dependencies of other services or deliberately kept off the public edge, reachable only over loopback or the tailnet.
+All served via Caddy. WordPress sites run in isolated Docker stacks:
-| Exposure tier | How it's reached | Example surface |
-|---|---|---|
-| Public | Caddy → Authentik → app | Atlas OS desktop, public sites |
-| Loopback | Bound to localhost; proxied via authed Caddy route only | Backend dependencies |
-| Tailnet | `tailscale serve`, tailnet members only | Internal-only services |
-
-The **Remote Desktop** (Webtop `ubuntu-xfce`, Selkies/WebRTC) at `desktop.atlascorporation.nl` is a good illustration of the edge model in practice: it sits behind Authentik SSO, and ships with Claude Code + Chromium pre-installed so OAuth logins work in a real browser. See [[Remote-Desktop]].
+| Domain | Stack | Notes |
+|--------|-------|-------|
+| `atlasworks.nl` | Docker: works-wordpress | Live WooCommerce (Caddy :8111) |
+| `atlascorporation.nl` | Docker: corp-wordpress | Corporate site |
+| `atlasagency.nl` | Docker: agency-wordpress | Agency services |
+| `atlasacademy.nl` | TransIP nginx (static HTML) | Academy landing |
+| `atlaspos.nl` | TransIP | AtlasPOS product site |
---
-## Data flows through the Mind
+## Data Flow — Atlas Brain Cycle
-Atlas is not just a pile of apps behind a proxy — it has a reasoning core, and operational data flows through it.
-
-- **The spine is `kernel.sqlite`** — the canonical store of decisions and events (~458 decisions, ~3415 events recorded).
-- **Atlas Brain** is the reviewed-execution autonomous core. It can act, but it acts under a gate.
-- **The policy gate ALWAYS requires human approval** for anything touching **money**, **external communications**, or **self-modification**. There is no autonomous path around it.
-- **The LLM router at `localhost:8888`** picks the cheapest capable model for each task, escalating only when needed:
-
-```mermaid
-%%{init:{'theme':'base','themeVariables':{'primaryColor':'#0c1428','primaryTextColor':'#cfe2ff','primaryBorderColor':'#1F6FE0','lineColor':'#4EA0FF'}}}%%
-flowchart LR
- Q[Task] --> R{LLM router
localhost:8888}
- R -->|first| LOC[Local models
LLaMA 1B · hermes3 · Qwen Coder · Qwen 7B]
- R -->|then| FREE[Free cloud
Groq llama-3.3-70b · Cerebras · OpenRouter]
- R -->|last| PAID[Claude — paid]
- LOC --> H[Headroom proxy
context compression]
- FREE --> H
- PAID --> H
+```
+Every 15 minutes (atlas-brain.service):
+ ┌──────────────────────────────────────────┐
+ │ Signal collection phase │
+ │ ├── 7 IMAP mailboxes (read-only) │
+ │ ├── Odoo REST API (leads, contacts) │
+ │ ├── Enable Banking (bank transactions) │
+ │ └── atlas-01 health/fleet feeds │
+ └──────────────────┬───────────────────────┘
+ │
+ ▼
+ atlas-state.json
+ │
+ ▼
+ LLM synthesis (model router):
+ atlas-auto (local) → Groq 70B → Claude Opus
+ │
+ ▼
+ atlas-brief-spoken.json (daily brief)
+ │
+ ▼
+ Atlas OS cockpit panels (Today · Money · Inbox · Pipeline)
```
-The router binds locally (`localhost:8888`) — itself an example of the loopback-binding principle: the model router is an internal service, not a public endpoint. The **Headroom proxy** compresses context so requests stay cheap. Local models are tried first, free cloud tiers next, and Claude (paid) only as a last resort.
+---
-For how the Mind, the policy gate, and the router fit together operationally, see [[AI-Stack]] and [[Atlas-Brain]].
+## The AI Mesh
+
+```
+atlas-01 (NATS JetStream server)
+ │
+ ├── subjects:
+ │ atlas.events.* system events (disk, CPU, service state)
+ │ atlas.heartbeat.* device health pings
+ │ atlas.jobs.* delegated compute jobs (Ollama on NEBULA)
+ │ atlas.brief.* daily brief push to all devices
+ │
+ └── subscribers:
+ Caddy PC (this machine) ← atlas-events-collector
+ NEBULA (HP EliteBook) ← atlas-delegate-worker (5 Ollama models)
+ MET-Desktop ← Odoo failover + fleet monitor
+```
---
-## Why this holds together
+## Caddy SSO Pattern
-| Principle | Implementation |
-|---|---|
-| One failure domain, one control plane | Single Hetzner VPS, ~55 containers |
-| Sovereign data | EU hosting (NL/DE), no US Cloud Act exposure |
-| One front door | Caddy terminates TLS for every hostname |
-| One identity gate | Authentik `forward_auth` on the embedded outpost |
-| Small public surface | Loopback binding + `tailscale serve` keep internals private |
-| Cheapest-capable compute | LLM router: local → free cloud → Claude last |
-| No silent autonomy on what matters | Policy gate requires human approval for money / comms / self-modify |
+Every SSO-gated service uses this Caddyfile block (see `deploy/caddy/Caddyfile.template`):
+
+```caddy
+app.example.com {
+ # Authentik outpost endpoints (must come first)
+ reverse_proxy /outpost.goauthentik.io/* authentik-server:9000
+
+ # SSO gate — redirects to login if no valid session
+ forward_auth authentik-server:9000 {
+ uri /outpost.goauthentik.io/auth/caddy
+ copy_headers X-Authentik-Username X-Authentik-Groups X-Authentik-Email
+ X-Authentik-Name X-Authentik-Uid X-Authentik-Jwt
+ }
+
+ # Only reached after successful authentication
+ reverse_proxy localhost:APP_PORT
+}
+```
---
-### See also
+## Docker Compose Topology
-- [[Atlas-OS]] — the 39-app browser desktop
-- [[AI-Stack]] — the Mind, the router, and the policy gate
-- [[Security]] — EU-sovereign hosting, MFA, backups, secrets
-- [[Fleet-and-Mesh]] — MeshCentral, Tailscale spine, NATS
-- [[Remote-Desktop]] — Webtop behind SSO
+| Stack | File | Services |
+|-------|------|----------|
+| Core | `deploy/compose/atlas-core.yml` | Authentik, Nextcloud, Stalwart, Forgejo, Vaultwarden |
+| AI | `deploy/compose/atlas-ai.yml` | LiteLLM, Open-WebUI, Langfuse |
+| Business | `atlas-odoo.yml` | Odoo 19 + PostgreSQL |
+| Finance | `atlas-finance.yml` | Firefly III + Paperless-ngx + Enable Banking |
+| Fleet | `atlas-fleet.yml` | MeshCentral + NATS + Uptime Kuma |
+| Sites | Per-site compose | WordPress stacks |
+| Desktop | `deploy/webtop-docker-compose.yml` | Webtop (tailnet-only) |
+
+All stacks share the `atlas-net` Docker network. Caddy is the **sole** process with
+public port bindings (80/443). All internal services bind only to `127.0.0.1:PORT`.