{
    email {$ACME_EMAIL}
    admin off

    # Reduce protocol/parser attack surface without interfering with Guacamole
    # WebSockets. HTTP/3 can be enabled later if there is a concrete need.
    servers {
        protocols h1 h2
        strict_sni_host on
        max_header_size 64KB
        timeouts {
            read_header 10s
        }
    }
}

(edge_security_headers) {
    header {
        -Server
        Strict-Transport-Security "max-age=31536000"
        X-Content-Type-Options "nosniff"
        Referrer-Policy "strict-origin-when-cross-origin"
        Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=()"
    }
}

(edgeguard_check) {
    # EdgeGuard only listens on 127.0.0.1. These headers are overwritten by
    # Caddy and therefore cannot be forged by an Internet client.
    forward_auth 127.0.0.1:9081 {
        uri /check
        header_up X-Edge-Client-IP {client_ip}
        header_up X-Edge-Original-Host {host}
        header_up X-Edge-Original-Method {method}
        header_up X-Edge-Original-URI {uri}
    }
}

# Guacamole public endpoint + SessionGuard Access Auth
{$GUAC_HOST} {
    encode zstd gzip
    import edge_security_headers

    route {
        # Security pre-check occurs before OIDC/Auth and before any backend.
        import edgeguard_check

        # These endpoints belong to SessionGuard, but intentionally live on
        # the Guacamole hostname so the Access-Auth cookie remains host-bound.
        handle_path /_sessionguard/* {
            reverse_proxy {$SESSIONGUARD_IP}:8080
        }

        handle {
            route {
                # Never trust identity headers supplied by an Internet client.
                request_header -X-Guacamole-User
                request_header -X-SessionGuard-User
                request_header -X-SessionGuard-Email
                request_header -X-SessionGuard-Groups
                request_header -X-Forwarded-User
                request_header -X-Authenticated-User

                # SessionGuard is the single OIDC/ForwardAuth authority.
                forward_auth {$SESSIONGUARD_IP}:8080 {
                    uri /auth/verify
                    copy_headers {
                        X-Guacamole-User
                        X-SessionGuard-User
                        X-SessionGuard-Email
                        X-SessionGuard-Groups
                    }
                }

                # Sticky sessions are important because Guacamole keeps runtime
                # authentication/session state in the selected webapp process.
                reverse_proxy {$GUAC01_IP}:8080 {$GUAC02_IP}:8080 {$GUAC03_IP}:8080 {
                    lb_policy cookie guac_node {$GUAC_LB_SECRET}
                    lb_try_duration 5s
                    lb_try_interval 250ms
                    health_uri /
                    health_interval 10s
                    health_timeout 3s
                }
            }
        }
    }

    log {
        output file /var/log/caddy/guacamole-access.log {
            roll_size 100MiB
            roll_keep 5
            roll_keep_for 168h
        }
        format json
        sampling {
            interval 1s
            first 200
            thereafter 20
        }
    }
}

# SessionGuard administration UI, Agent endpoint and APIs.
{$SESSIONGUARD_HOST} {
    encode zstd gzip
    import edge_security_headers

    route {
        import edgeguard_check

        # These endpoints are required internally only. Broker requests from
        # Guacamole workers go directly over NetBird, never via public Caddy.
        respond /metrics 404
        respond /api/v1/broker/* 404

        reverse_proxy {$SESSIONGUARD_IP}:8080
    }

    log {
        output file /var/log/caddy/sessionguard-access.log {
            roll_size 100MiB
            roll_keep 5
            roll_keep_for 168h
        }
        format json
        sampling {
            interval 1s
            first 200
            thereafter 20
        }
    }
}
