# ============================================================================= # ai-routes.caddy - the blocks to paste into ~/Caddyfile on lin001. # # This file is NOT deployed as-is. ~/Caddyfile is a single hand-maintained file # with many .bak-* snapshots beside it. Append these blocks, then: # # cp ~/Caddyfile ~/Caddyfile.bak-ai-$(date +%Y%m%d) # docker exec caddy caddy reload --config /etc/caddy/Caddyfile # # `import authelia` is the shared AD + Duo gate. OMITTING IT SILENTLY MAKES THE # SERVICE PUBLIC. Every block below keeps it - there is no deliberate exception # anywhere in this stack. (Forgejo omits it only because forward-auth breaks # git clients; that reason does not apply to anything here.) # # DNS is not managed on this host. Each hostname needs an A record -> # 20.211.144.151 before Caddy can issue a certificate. Ask Dan. # # DO NOT ADD A HOSTNAME THIS PROJECT IS NOT BLOCKED WITHOUT. lin001 is shared, # already carries a dozen *.yokogawa.tech services, and other people have to # work out which name serves what. The cost of a name is not the DNS change, it # is the attribution problem it leaves behind. Prefer a path route under a name # that already exists - which is what /ask under ai.yokogawa.tech is. # # What this project owns, and what each one is for: # # ai.yokogawa.tech the operator's screen, and /ask behind it. The ONLY one # a control-room PC needs, and the only one with a DC # pinpoint record. # api.yokogawa.tech ai-api direct. Used by the eval runner and, at Phase 9, # by the /docs endpoints - whose publisher rule is scoped # to THIS hostname. Not used by the operator's browser. # cube.yokogawa.tech Cube's playground and REST API, for an engineer # hand-verifying a measure. ai-api reaches Cube # container-to-container and never by hostname, so # nothing breaks if this one is dropped. # lf.yokogawa.tech Langfuse - traces, prompts, eval runs. # # DONE 2026-08-27: ai, api and cube .yokogawa.tech all resolve to # 20.211.144.151, and the DC carries the pinpoint record # ai.yokogawa.tech -> 10.0.0.17 for the Azure hairpin (same treatment as # influx.yokogawa.tech). api and cube have NO pinpoint record and are # unresolvable from inside the VNet - which is why /ask is routed under # ai.yokogawa.tech below rather than called cross-origin. # # Add each block at the phase that needs it, not all at once. A hostname with a # Caddyfile block and no Authelia rule is a hole. # ============================================================================= # --- Phase 2 ----------------------------------------------------------------- lf.yokogawa.tech { import authelia reverse_proxy langfuse:3000 } # --- Phase 5 ----------------------------------------------------------------- cube.yokogawa.tech { import authelia reverse_proxy cube:4000 } # --- Phase 6 ----------------------------------------------------------------- api.yokogawa.tech { import authelia reverse_proxy ai-api:8000 } # --- Phase 9 ----------------------------------------------------------------- # Document upload needs two changes to the block above. No new hostname: the # upload endpoints live under /docs on the existing API. # # 1. ai-api must learn WHO is calling. Authelia returns Remote-User, # Remote-Name, Remote-Email and Remote-Groups from the forward-auth # subrequest; Caddy only passes them upstream if it is told to. # # CHECK ~/Caddyfile's shared `authelia` snippet FIRST. If it already sets # copy_headers, this is done and duplicating it is a no-op at best. Do NOT # edit the shared snippet to add it - every other service on the host # imports it, and this is not the change to make on their behalf. # # CHECKED 2026-08-27: the snippet on lin001 is # forward_auth authelia:9091 { # uri /api/authz/forward-auth # copy_headers Remote-User Remote-Groups Remote-Name Remote-Email # } # It already copies all four headers, so Phase 9 needs NO change here - # only request_body if 50MB uploads are wanted. Note the uri differs from # the one written below. Re-check before Phase 9 rather than trusting this # line; it is a file other people edit. # # If the snippet does not copy them, replace the Phase 6 block with: # # api.yokogawa.tech { # forward_auth authelia:9091 { # uri /api/verify?rd=https://auth.yokogawa.tech # copy_headers Remote-User Remote-Name Remote-Email Remote-Groups # } # request_body { # max_size 50MB # match MAX_UPLOAD_MB; Caddy refuses larger # } # reverse_proxy ai-api:8000 # } # # - matching the shared snippet's own forward_auth arguments, which must be # read off the host rather than assumed from this comment. # # 2. ai-api trusts those headers only because nothing outside the proxy network # can reach it. If anything here ever gains a published port, that trust is # gone and the /docs endpoints are open to whoever can reach the port. # # The group restriction is NOT expressed here. It goes in the Authelia rule for # ^/docs/.* (authelia/access-rules.md), and is re-checked in ai-api. A Caddy # matcher would be a third place to keep in step, and the first to be forgotten. # --- Phase 7 ----------------------------------------------------------------- # /ask is served under THIS hostname as well as under api.yokogawa.tech, so the # operator's page is same-origin. That is not a convenience: LAN hosts resolve # yokogawa.tech names through the DC, which holds pinpoint records only, and # there is one for ai.yokogawa.tech and none for api.yokogawa.tech. Cross-origin # means an operator on cicore1 loads the page and every question fails on DNS. # # ONLY /ask. Do NOT widen this matcher. # - The Phase 9 publisher rule is `domain: api.yokogawa.tech` + # `resources: ^/docs/.*` (authelia/access-rules.md). A /docs route under this # hostname would not match it, and approving a procedure revision would stop # being gated on AI_DocPublishers. ai-api re-checks Remote-Groups itself, so # it would not actually fail open - but the rule would be inert and nobody # would know. # - `/docs*` under this hostname hits the second handle and is served by # ai-web, which 404s it. That is the intended outcome. Leave it that way. # # `route` is required. `import authelia` expands to forward_auth, which sorts # AFTER handle in Caddy's default directive order - outside a route block the # handles would be terminal and the gate would never run, silently serving the # UI to anyone. Inside `route`, directives run in written order. ai.yokogawa.tech { route { import authelia handle /ask* { reverse_proxy ai-api:8000 } handle { reverse_proxy ai-web:80 } } }