# Plain-HTTP variant of deploy/traefik/dynamic.yml (DEVGRAIL_TLS=off).
#
# Identical routing to the TLS config, but every router binds the plain `web`
# entrypoint with no TLS/certResolver and there is no HTTP→HTTPS redirect — the
# catch-all devgrail-web router serves directly on :80. Use only on a private
# network or behind an external TLS terminator; anything public should run the
# default TLS config (dynamic.yml). The install/deploy scripts pick this file
# when DEVGRAIL_TLS=off. The internal MCP entrypoint is unchanged (it is not a
# TLS concern). Keep this in sync with dynamic.yml.
http:
  routers:
    # Single public host: the one Go binary serves the UI, REST API, browser
    # terminal (/ws/<seg>/, via container labels) and MCP (/mcp) all from here.
    devgrail-web:
      rule: 'Host(`__DEVGRAIL_DOMAIN__`)'
      entryPoints: [web]
      service: devgrail-host

    # Public MCP endpoint for external AI agents. Higher priority than the
    # catch-all web router so /mcp is tagged as external. The mcp-public
    # middleware overwrites X-DevGrail-Forwarded, so a client cannot spoof it.
    devgrail-mcp:
      rule: 'Host(`__DEVGRAIL_DOMAIN__`) && (Path(`/mcp`) || PathPrefix(`/mcp/`))'
      priority: 100
      entryPoints: [web]
      service: devgrail-host
      middlewares: [mcp-public]

    # OAuth 2.1 authorization server + discovery for the Claude.ai MCP connector
    # (internal/oauth). Public, no ForwardAuth (consent gates on the
    # devgrail_session cookie in-app). Higher priority than the catch-all web
    # router; distinct paths from /api and /mcp so no conflict.
    devgrail-oauth:
      rule: 'Host(`__DEVGRAIL_DOMAIN__`) && (PathPrefix(`/oauth/`) || PathPrefix(`/.well-known/oauth-`))'
      priority: 100
      entryPoints: [web]
      service: devgrail-host

    # Traefik dashboard UI at /traefik/, gated behind admin auth.
    # traefik-dashboard-rewrite maps /traefik back to /dashboard before it reaches
    # api@internal.
    traefik-dashboard:
      rule: 'Host(`__DEVGRAIL_DOMAIN__`) && PathPrefix(`/traefik`)'
      priority: 150
      entryPoints: [web]
      service: api@internal
      middlewares: [devgrail-admin-forwardauth, traefik-dashboard-rewrite]

    # The dashboard SPA hard-codes its data API at the root /api/, so route that to
    # api@internal too. Same admin gate as the UI router.
    traefik-dashboard-api:
      rule: 'Host(`__DEVGRAIL_DOMAIN__`) && PathPrefix(`/api`)'
      priority: 150
      entryPoints: [web]
      service: api@internal
      middlewares: [devgrail-admin-forwardauth]

    # Higher priority sends /api/v1 back to the app; only the remaining /api/*
    # (the dashboard's data calls) hit api@internal.
    devgrail-api:
      rule: 'Host(`__DEVGRAIL_DOMAIN__`) && PathPrefix(`/api/v1`)'
      priority: 200
      entryPoints: [web]
      service: devgrail-host

    # Internal MCP endpoint for AI agents running inside DevGrail workspaces. Bound
    # only to the unpublished "mcpinternal" entrypoint (:9100), so it is reachable
    # solely by containers on the edge network — never from the public internet.
    # The mcp-internal middleware strips X-DevGrail-Forwarded, so these requests are
    # treated as internal. Any Host matches: in-container agents target the Traefik
    # service name directly.
    devgrail-mcp-internal:
      rule: 'Path(`/mcp`) || PathPrefix(`/mcp/`)'
      entryPoints: [mcpinternal]
      service: devgrail-host
      middlewares: [mcp-internal]

  services:
    devgrail-host:
      loadBalancer:
        servers:
          - url: "http://devgrail-server:12080"

  middlewares:
    devgrail-forwardauth:
      forwardAuth:
        address: "http://devgrail-server:12080/auth/forwardauth"
        trustForwardHeader: true

    # Gates the Traefik dashboard: the server validates the forwarded session
    # cookie and allows only admins. The Cookie header is forwarded with the
    # request, so no extra auth header configuration is needed.
    devgrail-admin-forwardauth:
      forwardAuth:
        address: "http://devgrail-server:12080/auth/dashboard-forwardauth"
        trustForwardHeader: true

    # Maps the public /traefik prefix onto /dashboard, the path the dashboard
    # bundle is internally served at. Slash-optional so both /traefik and
    # /traefik/ (and sub-paths) resolve.
    traefik-dashboard-rewrite:
      replacePathRegex:
        regex: "^/traefik/?(.*)"
        replacement: "/dashboard/$1"

    # Marks requests as external (public) MCP access. Always overwrites the
    # header so a client-supplied value cannot impersonate internal traffic.
    mcp-public:
      headers:
        customRequestHeaders:
          X-DevGrail-Forwarded: "public"

    # Strips X-DevGrail-Forwarded on the internal entrypoint so a workspace cannot
    # supply its own value; an absent header is what the server treats as
    # internal. (Setting a custom request header to "" removes it in Traefik.)
    mcp-internal:
      headers:
        customRequestHeaders:
          X-DevGrail-Forwarded: ""
