--- # Stable exposure contracts. VM addresses and credentials belong to private inventory. version: 1 namespaces: mcp: {mesh: false} istio-system: {mesh: false} pharos: {mesh: false} argocd: {mesh: false} pharos-aai: {mesh: false} rook-ceph: {mesh: false} gitlab: {mesh: false} waldur: {mesh: false} themelio-compat: {mesh: false} llm: {mesh: false} zammad: {mesh: false} oauth2-proxy: {mesh: false} kubeflow: {mesh: false} gateways: shared: {namespace: istio-system} services: mcp-gateway: host: mcp.pharos path: /mcp profile: mcp gateway: shared access: public transport: http backend: {namespace: mcp, service: mcp-gateway, port: 8082, protocol: http} websocket: false timeout: 300s body: {limit: 64k, buffering: false} mcp-discovery: host: mcp.pharos path: /.well-known/oauth-protected-resource/mcp profile: mcp gateway: shared access: public transport: http backend: {namespace: mcp, service: mcp-gateway, port: 8082, protocol: http} websocket: false timeout: 60s mcp-broker: host: mcp.pharos path: / profile: mcp gateway: shared access: public transport: http backend: {namespace: mcp, service: mcp-broker, port: 8083, protocol: http} websocket: false timeout: 60s body: {limit: 64k, buffering: true} # The web service is the public composition for the product UI, including its # same-origin API and realtime proxies. The API host exposes only reviewed public paths. web: host: web.pharos path: / profile: pharos gateway: shared access: public transport: http backend: {namespace: pharos, service: web, port: 8080, protocol: http} websocket: true timeout: 300s body: {limit: 64k, buffering: true} # The platform origin serves the browser shell. The Site API has its own # hostname and exact native prefixes. platform: host: platform.pharos path: / profile: platform gateway: shared access: public transport: http backend: {namespace: pharos, service: web, port: 8080, protocol: http} websocket: true timeout: 300s body: {limit: 64k, buffering: true} api: host: api.pharos path: /api/v1/ profile: pharos gateway: shared access: public transport: http backend: {namespace: pharos, service: api, port: 8080, protocol: http} websocket: false timeout: 60s body: {limit: 64k, buffering: true} api-auth: host: api.pharos path: /auth/ profile: pharos gateway: shared access: public transport: http backend: {namespace: pharos, service: api, port: 8080, protocol: http} websocket: false timeout: 60s body: {limit: 64k, buffering: true} api-health: host: api.pharos path: /healthz profile: pharos gateway: shared access: public transport: http backend: {namespace: pharos, service: api, port: 8080, protocol: http} websocket: false timeout: 60s site-api-commands: host: site.pharos path: /v1/site/commands/ profile: pharos-site gateway: shared access: public transport: http backend: {namespace: pharos, service: site-api, port: 8443, protocol: https, tls: {trust: pharos-site-api-trust, sni: site-api.pharos.svc, subjectAltName: site-api.pharos.svc}} websocket: false timeout: 60s body: {limit: 64k, buffering: true} site-api-run-logs: host: site.pharos path: /v1/site/commands/run-logs/ profile: pharos-site gateway: shared access: public transport: http backend: {namespace: pharos, service: site-api, port: 8443, protocol: https, tls: {trust: pharos-site-api-trust, sni: site-api.pharos.svc, subjectAltName: site-api.pharos.svc}} websocket: false timeout: 60s body: {limit: 5m, buffering: true} site-api-ssh-access: host: site.pharos path: /v1/site/ssh-access/ profile: pharos-site gateway: shared access: public transport: http backend: {namespace: pharos, service: site-api, port: 8443, protocol: https, tls: {trust: pharos-site-api-trust, sni: site-api.pharos.svc, subjectAltName: site-api.pharos.svc}} websocket: false timeout: 60s body: {limit: 64k, buffering: true} site-api-executions: host: site.pharos path: /v1/site/executions/ profile: pharos-site gateway: shared access: public transport: http backend: {namespace: pharos, service: site-api, port: 8443, protocol: https, tls: {trust: pharos-site-api-trust, sni: site-api.pharos.svc, subjectAltName: site-api.pharos.svc}} websocket: false timeout: 60s body: {limit: 64k, buffering: true} site-api-repositories: host: site.pharos path: /v1/site/repositories/ profile: pharos-site gateway: shared access: public transport: http backend: {namespace: pharos, service: site-api, port: 8443, protocol: https, tls: {trust: pharos-site-api-trust, sni: site-api.pharos.svc, subjectAltName: site-api.pharos.svc}} websocket: false timeout: 60s body: {limit: 64k, buffering: true} aai: host: pharos path: /auth/ profile: aai gateway: shared access: public transport: http backend: {namespace: pharos-aai, service: keycloak, port: 8080, protocol: http} websocket: false timeout: 60s ceph-dashboard: host: ceph.pharos path: / profile: storage gateway: shared access: management transport: http backend: {namespace: rook-ceph, service: rook-ceph-mgr-dashboard, port: 8443, protocol: https, tls: {trust: pharos-ceph-trust, sni: ceph-dashboard}} websocket: false timeout: 60s argocd: host: argo.pharos path: / profile: argocd gateway: shared access: management transport: http backend: {namespace: argocd, service: argocd-server, port: 80, protocol: http} websocket: true timeout: 300s # Git over HTTPS and LFS: Workhorse streams unbounded uploads, so the edge must not buffer them. gitlab: host: gitlab.pharos path: / profile: gitlab gateway: shared access: public transport: http backend: {namespace: gitlab, service: gitlab-webservice-default, port: 8181, protocol: http} websocket: false timeout: 3600s body: {limit: '0', buffering: false} waldur: host: waldur.pharos path: / profile: waldur gateway: shared access: public transport: http backend: {namespace: waldur, service: waldur-homeport, port: 80, protocol: http} websocket: false timeout: 60s waldur-api: host: waldur.pharos path: /api/ profile: waldur gateway: shared access: public transport: http backend: {namespace: waldur, service: waldur-mastermind-api, port: 80, protocol: http} websocket: false timeout: 300s body: {limit: 64m, buffering: true} waldur-api-auth: host: waldur.pharos path: /api-auth/ profile: waldur gateway: shared access: public transport: http backend: {namespace: waldur, service: waldur-mastermind-api, port: 80, protocol: http} websocket: false timeout: 300s waldur-static: host: waldur.pharos path: /static/ profile: waldur gateway: shared access: public transport: http backend: {namespace: waldur, service: waldur-mastermind-api, port: 80, protocol: http} websocket: false timeout: 300s waldur-health: host: waldur.pharos path: /health-check/ profile: waldur gateway: shared access: public transport: http backend: {namespace: waldur, service: waldur-mastermind-api, port: 80, protocol: http} websocket: false timeout: 300s waldur-admin: host: waldur.pharos path: /admin/ profile: waldur gateway: shared access: management transport: http backend: {namespace: waldur, service: waldur-mastermind-api, port: 80, protocol: http} websocket: false timeout: 300s # Pending: the Kubeflow browser front door. The kubeflow-core component's oauth2-proxy is the # gateway's ext_authz provider, not a reverse proxy: it answers a check and the gateway forwards # the request to the dashboard it protects. The route, its certificate and its DNS name are # prepared here but stay disabled until that component is delivered, so no environment can # select this profile yet. kubeflow: host: kubeflow.pharos path: / profile: kubeflow gateway: shared access: public transport: http backend: {namespace: kubeflow, service: dashboard, port: 80, protocol: http} websocket: true timeout: 300s # Every request on this host is checked by the proxy, which returns the GRNET login redirect # and the `kubeflow-userid` header the dashboard trusts. auth: provider: oauth2-proxy notPaths: [/oauth2/] identityHeaders: [kubeflow-userid, x-auth-request-user] # The dashboard's Notebooks entry is a link, not a proxy: the vendored dashboard answers `/*` with its own # page, so `/jupyter/` has to reach the web app through the gateway. The app runs with `APP_PREFIX=/jupyter`, # so the prefix is rewritten away and reported to it as `x-forwarded-prefix`, and the same login check that # protects the dashboard protects the spawner form. kubeflow-jupyter: host: kubeflow.pharos path: /jupyter/ profile: kubeflow gateway: shared access: public transport: http backend: {namespace: kubeflow, service: jupyter-web-app-service, port: 80, protocol: http} websocket: false timeout: 300s rewrite: {uri: /, forwarded_prefix: /jupyter} auth: provider: oauth2-proxy notPaths: [/oauth2/] identityHeaders: [kubeflow-userid, x-auth-request-user] # The login endpoints themselves: the proxy redirects here and GRNET calls back, so these paths # reach the proxy directly and are the only ones its ext_authz check does not cover. kubeflow-login: host: kubeflow.pharos path: /oauth2/ profile: kubeflow gateway: shared access: public transport: http backend: {namespace: oauth2-proxy, service: oauth2-proxy, port: 4180, protocol: http} websocket: false timeout: 60s themelio-compat: host: themelio-compat.pharos path: / profile: themelio-compat gateway: shared access: public transport: http backend: {namespace: themelio-compat, service: web, port: 80, protocol: http} websocket: true # The retained web streams model and dataset files to GitLab. git: {service: gitlab} timeout: 3600s body: {limit: '0', buffering: false} # Chat. Open WebUI streams replies and keeps a WebSocket open, so no request timeout. Its administration is # restricted by the application's own roles; the gateway's management interface is not routed at all. open-webui: host: open-webui.pharos path: / profile: llm-chat gateway: shared access: public transport: http backend: {namespace: llm, service: open-webui, port: 80, protocol: http} websocket: true timeout: 300s # The member inference surface only. LiteLLM's management routes (/key, /team, /user, /model, /ui) # and its other /v1 paths (model info, files, batches, fine-tuning, assistants, threads, vector stores, # containers, videos, skills, agents, RAG) share its port and are never routed. Each path below is an # exact prefix the edge may serve; everything else is the 404 fallback. llm-api: host: llm-api.pharos path: /v1/models profile: llm-api gateway: shared access: public transport: http backend: {namespace: llm, service: litellm, port: 4000, protocol: http} websocket: false timeout: 900s llm-api-chat: host: llm-api.pharos path: /v1/chat/completions profile: llm-api gateway: shared access: public transport: http backend: {namespace: llm, service: litellm, port: 4000, protocol: http} websocket: false timeout: 900s llm-api-completions: host: llm-api.pharos path: /v1/completions profile: llm-api gateway: shared access: public transport: http backend: {namespace: llm, service: litellm, port: 4000, protocol: http} websocket: false timeout: 900s llm-api-embeddings: host: llm-api.pharos path: /v1/embeddings profile: llm-api gateway: shared access: public transport: http backend: {namespace: llm, service: litellm, port: 4000, protocol: http} websocket: false timeout: 900s # OpenAI's Responses API is part of the compatible surface and its objects are scoped to the key. llm-api-responses: host: llm-api.pharos path: /v1/responses profile: llm-api gateway: shared access: public transport: http backend: {namespace: llm, service: litellm, port: 4000, protocol: http} websocket: false timeout: 900s # Claude Code speaks the Anthropic Messages API; the same prefix carries its token count. llm-api-messages: host: llm-api.pharos path: /v1/messages profile: llm-api gateway: shared access: public transport: http backend: {namespace: llm, service: litellm, port: 4000, protocol: http} websocket: false timeout: 900s # Zammad's nginx serves the app, its API and the websocket; attachments are up to 50 MB. # Only staff use it (Pharos users work through Support), so it is a management route: # the environment's management networks, or the SSH tunnel on a dev box. zammad: host: zammad.pharos path: / profile: zammad gateway: shared access: management transport: http backend: {namespace: zammad, service: zammad-nginx, port: 8080, protocol: http} websocket: true timeout: 300s body: {limit: 50m, buffering: true}