api.yokogawa.tech has a public A record but no pinpoint record on the DC, so it does not resolve from inside the VNet at all. The browser called it by hostname, which means an operator on cicore1 would have loaded the page and had every question fail on DNS - the exact gap Phase 7's gate exists to catch, and one an engineer's laptop cannot see. Caddy now routes /ask under ai.yokogawa.tech to ai-api, inside a route block so import authelia still runs first: forward_auth sorts after handle in the default directive order, and outside a route the handles would be terminal and the gate would never run. Only /ask is routed - the Phase 9 publisher rule is scoped to api.yokogawa.tech and a wider route here would leave it inert. Also fixes the fallback it replaces. The build arg defaults to "", and `?? "https://api.yokogawa.tech"` does not catch an empty string, so the documented real-deployment build resolved the API base to "" and posted /ask at ai-web, which 404s it. verify.sh and deploy.sh now check both the route and whether the built bundle carries the hostname. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
125 lines
5.6 KiB
Text
125 lines
5.6 KiB
Text
# =============================================================================
|
|
# 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.
|
|
#
|
|
# 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
|
|
}
|
|
}
|
|
}
|