{
  "title": "Public Routing and Traefik Flow",
  "summary": "A clear explanation of how public web requests move through this VPS: DNS and HTTPS entry, Traefik router selection, Docker-label routing, file-provider routing for host-backed services, and the extra internal path routing used by the brainstorming app.",
  "pattern": "system overview",
  "sections": [
    {
      "heading": "Entry point",
      "body": "Public web traffic enters through Traefik, which is the only container binding host ports 80 and 443. HTTP requests are redirected to HTTPS, and certificate issuance is handled through the Let's Encrypt resolver configured in Traefik.",
      "bullets": [
        "Traefik binds 80 and 443 on the host",
        "web redirects to websecure",
        "TLS is handled by the le ACME resolver"
      ]
    },
    {
      "heading": "How Traefik chooses a destination",
      "body": "Traefik uses host-based routing. For most public apps, Docker labels on the target container declare the hostname, entrypoint, TLS requirement, and internal service port. Once the hostname matches, Traefik forwards the request to that container.",
      "bullets": [
        "Docker provider is enabled",
        "exposedByDefault is false, so services must opt in",
        "Examples routed by labels: brainstorming.ovidiutm.com, ovidiutm.com, spendiq.ovidiutm.com, spendiq-dev.ovidiutm.com"
      ]
    },
    {
      "heading": "Docker-label route examples",
      "body": "The brainstorming container advertises both brainstorming.ovidiutm.com and vps-monitor.ovidiutm.com through Traefik labels, while the root-site container advertises ovidiutm.com. Spendiq production and dev use runtime compose stacks under /opt/platform/apps and also attach their public web services through labels.",
      "bullets": [
        "brainstorming container -> brainstorming.ovidiutm.com",
        "brainstorming container also fronts vps-monitor.ovidiutm.com",
        "ovidiutm-com container -> ovidiutm.com",
        "spendiq and spendiq-dev web containers -> their own public hostnames"
      ]
    },
    {
      "heading": "File-provider route for code-server",
      "body": "Not every public surface comes from Docker labels. vscode.ovidiutm.com is defined in Traefik's dynamic file provider and points to a host-backed service URL at 172.17.0.1:8080, which is where code-server listens from the host side.",
      "bullets": [
        "Dynamic route file: /opt/platform/traefik/dynamic/vscode.yml",
        "Target URL: http://172.17.0.1:8080",
        "This is the host-backed special case in the public flow"
      ]
    },
    {
      "heading": "What happens inside brainstorming",
      "body": "Traefik only decides that a request belongs to the brainstorming container. After that, the Caddy config inside the brainstorming app handles path-level behavior such as serving the root index, exposing status-board API calls, redirecting the vps-monitor host route, and providing source-browse routes for selected apps.",
      "bullets": [
        "Caddy serves /srv from the brainstorming workspace",
        "status-board API is reverse-proxied to 172.17.0.1:19192",
        "Source-browse routes for selected brainstorming apps are handled inside the brainstorming container"
      ]
    },
    {
      "heading": "Why this split works",
      "body": "The routing stack stays understandable because responsibilities are split cleanly. Traefik owns public ingress, TLS, and hostname selection. App containers own their own internal path logic. Host-backed services only need special handling when they are not naturally containerized.",
      "bullets": [
        "Traefik decides the host-level destination",
        "App web servers decide path-level behavior inside the selected app",
        "Host-backed services use file-provider or reverse-proxy exceptions when needed"
      ]
    },
    {
      "heading": "Current takeaway",
      "body": "The public routing model on this VPS is simple enough to reason about: one ingress, explicit host rules, minimal public port exposure, and only a small number of special cases. When adding a new public app, the main decision is whether it should be container-routed like the other apps or treated as a host-backed exception like code-server.",
      "bullets": [
        "Default new public apps to Docker + Traefik labels",
        "Use dynamic file routes only for real host-backed exceptions",
        "Keep path routing with the app whenever possible"
      ]
    }
  ]
}
