diff --git a/backend/internal/adminconsole/templates/layout.gohtml b/backend/internal/adminconsole/templates/layout.gohtml
index 8634190..8dd6676 100644
--- a/backend/internal/adminconsole/templates/layout.gohtml
+++ b/backend/internal/adminconsole/templates/layout.gohtml
@@ -17,6 +17,8 @@
Games
Operators
Mail
+ Grafana
+ Mailpit
{{.Username}}
diff --git a/tools/dev-deploy/Caddyfile.dev b/tools/dev-deploy/Caddyfile.dev
index bd485bf..462b9fe 100644
--- a/tools/dev-deploy/Caddyfile.dev
+++ b/tools/dev-deploy/Caddyfile.dev
@@ -29,28 +29,34 @@
reverse_proxy galaxy-api:8080
}
- # Operator console. Shares the gateway public listener with `/api`; the
- # gateway applies the admin anti-abuse class and reverse-proxies to the
- # backend `/_gm` surface, which enforces Basic Auth and renders the pages.
+ # Operator console + observability behind one Basic Auth gate. The gate
+ # credential equals the admin-console account (dev: gm / gm-dev-password),
+ # so Caddy forwards the same Authorization header to the backend `/_gm`
+ # surface (its own Basic Auth) and to Grafana/Mailpit — one prompt covers
+ # all three. The gateway applies the admin anti-abuse class to the console.
@gm path /_gm /_gm/*
handle @gm {
- reverse_proxy galaxy-api:8080
- }
-
- # Grafana (observability UI) under /grafana/ — Caddy sub-path mode
- # (Grafana set with GF_SERVER_SERVE_FROM_SUB_PATH); its own login.
- handle /grafana/* {
- reverse_proxy galaxy-grafana:3000
- }
-
- # Mailpit captured-mail UI under /mailpit/. Shows every message the
- # backend sent (relayed or not); basic-auth (dev: gm / gm-dev-password)
- # guards the OTP codes it exposes. Mailpit runs with MP_WEBROOT=/mailpit.
- handle /mailpit/* {
basic_auth {
gm "$2a$14$xVh1TLaZxh8fazlKrI9Mx.NQMQlMarYWtr3FRELmZIXuac/DeeTRO"
}
- reverse_proxy galaxy-mailpit:8025
+
+ # Grafana under /_gm/grafana/ (sub-path mode; anonymous Admin, so the
+ # /_gm gate is the only barrier — GF_AUTH_BASIC_ENABLED=false makes it
+ # ignore the forwarded Authorization header).
+ handle /_gm/grafana/* {
+ reverse_proxy galaxy-grafana:3000
+ }
+
+ # Mailpit captured-mail UI under /_gm/mailpit/ (MP_WEBROOT). Shows
+ # every message the backend sent, relayed or not.
+ handle /_gm/mailpit/* {
+ reverse_proxy galaxy-mailpit:8025
+ }
+
+ # The operator console itself (gateway -> backend /_gm surface).
+ handle {
+ reverse_proxy galaxy-api:8080
+ }
}
# Bare `/game` (no trailing slash) -> `/game/` so the SPA root
diff --git a/tools/dev-deploy/docker-compose.yml b/tools/dev-deploy/docker-compose.yml
index d9cf40e..cdb647d 100644
--- a/tools/dev-deploy/docker-compose.yml
+++ b/tools/dev-deploy/docker-compose.yml
@@ -74,9 +74,10 @@ services:
command:
- "--smtp-relay-config=/etc/mailpit/relay.conf"
- "--smtp-relay-matching=${GALAXY_DEV_MAIL_RELAY_MATCH:-nobody@invalid.example}"
- # Serve the capture UI under /mailpit so the host Caddy can expose it
- # at https://galaxy.lan/mailpit/ (behind basic-auth); SMTP is unaffected.
- - "--webroot=/mailpit"
+ # Serve the capture UI under /_gm/mailpit so the host Caddy can expose
+ # it at https://galaxy.lan/_gm/mailpit/ behind the shared /_gm gate;
+ # SMTP is unaffected.
+ - "--webroot=/_gm/mailpit"
labels:
galaxy.stack: dev-deploy
networks:
@@ -84,7 +85,7 @@ services:
volumes:
- galaxy-dev-mailpit-config:/etc/mailpit:ro
healthcheck:
- test: ["CMD", "wget", "-q", "-O-", "http://localhost:8025/mailpit/livez"]
+ test: ["CMD", "wget", "-q", "-O-", "http://localhost:8025/_gm/mailpit/livez"]
interval: 3s
timeout: 3s
retries: 30
@@ -412,8 +413,15 @@ services:
- galaxy-tempo
environment:
GF_SECURITY_ADMIN_PASSWORD: ${GALAXY_DEV_GRAFANA_ADMIN_PASSWORD:-admin}
- GF_SERVER_ROOT_URL: https://galaxy.lan/grafana/
+ GF_SERVER_ROOT_URL: https://galaxy.lan/_gm/grafana/
GF_SERVER_SERVE_FROM_SUB_PATH: "true"
+ # No own login: the /_gm Basic Auth gate is the only barrier, so
+ # serve everyone as anonymous Admin and ignore the forwarded
+ # Authorization header (basic auth off, login form off).
+ GF_AUTH_ANONYMOUS_ENABLED: "true"
+ GF_AUTH_ANONYMOUS_ORG_ROLE: Admin
+ GF_AUTH_DISABLE_LOGIN_FORM: "true"
+ GF_AUTH_BASIC_ENABLED: "false"
GF_USERS_ALLOW_SIGN_UP: "false"
GF_ANALYTICS_REPORTING_ENABLED: "false"
GF_ANALYTICS_CHECK_FOR_UPDATES: "false"