Files
public/docs/architecture-overview.md
T
thesabear 5a96e92dc6 docs: fix architecture-overview.md dependency graph rendering
Switch to LR layout, restore all 12 observability-reporting edges
(cut them by mistake, they belong in a dependency graph), drop the
repeated 'reports to' label text that was breaking legibility and
let line style (solid=dependency, dotted=async reporting) carry that
distinction instead. Also shortened edge labels on the trust-boundary
diagram to avoid the same clipping issue.
2026-08-22 12:40:50 +03:00

4.3 KiB

📐 Architecture Overview

Note

The stable, directional laws governing how traffic moves through the fleet — eight distinct traffic categories (ingress, east-west, secrets, DNS, observability, notifications, CI/CD, management), each with its own routing authority and its own failure behavior.

Dependency graph

flowchart LR
    Internet(["Internet"]) --> Edge["Edge Firewall"]
    Edge --> LAN["LAN"]

    LAN --> DNS["Internal DNS"]
    LAN --> Vault["Vault"]
    DNS --> Vault

    Vault --> Apps["App Host<br/>~40 stacks"]
    Vault --> Automation["Automation Host"]
    LAN --> FileServer["File Server"]
    DNS --> FileServer
    LAN --> Automation

    Apps --> InternalProxy["Internal Proxy Zone"]
    Apps --> SSO["SSO"]
    Automation --> GitHost["Git Hosting"]

    Vault --> EdgeProxy["User-Facing Proxy<br/>mTLS"]
    DNS --> EdgeProxy
    Apps -.proxied.-> EdgeProxy
    SSO -.auth.-> EdgeProxy

    Apps -.-> Monitoring["Monitoring"]
    Automation -.-> Monitoring
    FileServer -.-> Monitoring
    Vault -.-> Monitoring
    DNS -.-> Monitoring
    EdgeProxy -.-> Monitoring

    Apps -.-> SecMon["Security Monitoring"]
    Automation -.-> SecMon
    FileServer -.-> SecMon
    Vault -.-> SecMon
    DNS -.-> SecMon
    EdgeProxy -.-> SecMon

Tip

Solid arrows are real dependencies — the target has to be reachable and healthy for the source to function. Dotted arrows into Monitoring and Security Monitoring are the one exception: every host reports telemetry to both, but per the Observability Independence Law below, that reporting is asynchronous and non-blocking — nothing here actually depends on Monitoring or Security Monitoring being up. The line style carries that distinction instead of repeating "reports to" twelve times. Full detail on what's actually collected: docs/observability.md and docs/security-monitoring.md.

Four trust boundaries, not two

flowchart LR
    WAN["WAN"] -->|public| EdgeFW["Edge Firewall<br/>public proxy"]
    Browser["LAN browsers"] -->|mTLS + SSO| UserProxy["User-facing proxy"]
    Service["App-to-app"] -->|allowlist + SSO| InternalProxy["Internal proxy"]
    Infra["LAN infra-to-infra"] -->|per-flow rule| HostFW["Host firewall"]

Backend services trust proxy headers, not raw client IP, on every path.

Two structurally independent traffic planes

Plane Path
Ingress — public Client → edge firewall/proxy → backend
Ingress — LAN-facing Client → user-facing proxy (mTLS) → backend
East-west — app-to-app Service → internal proxy zone → service
East-west — infra-to-infra Service → firewall rule → service, direct, no proxy

Important

Observability sits outside every request path on purpose. Logs and metrics push asynchronously to monitoring infrastructure; nothing in that pipeline is inline with application traffic, and application services don't depend on monitoring availability. If monitoring goes down, collection stops — nothing else does.

Two real architecture changes, not a static design

Important

This shape isn't how the fleet started — it's evolved twice, deliberately. The public-facing reverse proxy used to run as its own dedicated VM; it was consolidated directly onto the edge firewall as a package instead, removing an entire host from the public-ingress path without losing any functionality — one less thing that can fail, one less thing to patch and monitor independently. And the internal service-to-service model used to be a flat, unauthenticated mesh network; it was fully replaced by the two-zone split in docs/dns-naming.md, trading a simpler-but-weaker model for one with a real trust boundary between browser-facing and app-to-app traffic. Both changes reduced the attack surface at the cost of more moving parts to reason about — a tradeoff made deliberately, not stumbled into.

What this map deliberately excludes

Not a firewall rule listing, not a port inventory, not a service configuration reference — those live in their own docs (docs/firewall-and-network.md and elsewhere). This is strictly the stable, directional shape of how trust and traffic actually move.


Related: docs/firewall-and-network.md, docs/dns-naming.md