Skip to main content

Internal Service Path Mappings — Use this guide when an internal service needs a browser/API URL under Portal ⬆ índice

Internal Service Path Mappings

Use this guide when an internal service needs a browser/API URL under Portal without publishing its own host port or public hostname.

Default pattern

Expose internal APIs under:

https://portal.desarrolloselectronicos.com/ext/<service>/<path>

Map that URL in Portal to the service's Docker-network URL:

http://<service>:<port>/<upstream-prefix>/<path>

Example:

/ext/user-service/tenants -> http://user-service:8000/api/v1/tenants

When to use Portal proxy

Use a Portal route handler when:

  • the service should stay internal;
  • callers already authenticate through Portal/Zitadel;
  • the upstream service is reachable on the shared Docker network;
  • the public URL should live below the Portal domain;
  • no dedicated browser app or iframe host is needed.

Do not publish a Docker host port only to expose an internal API.

When to use Traefik

Use Traefik when:

  • the service needs a dedicated public hostname;
  • the service must be accessed outside Portal auth/session context;
  • the service already has a safe localhost target port;
  • iframe/CSP behavior must be controlled at the public host level.

Traefik in this repo runs with network_mode: host, so generated upstreams use 127.0.0.1:<host-port>. A service with only expose: is not a Traefik target unless a host port is deliberately published.

Route handler checklist

  • Add route at platform/portal/ui/src/app/ext/<service>/[...path]/route.ts.
  • Require getServerSession(authOptions).
  • Require a usable access token from Portal session, inbound Authorization: Bearer ..., or a service credential.
  • Build upstream URL from a fixed base URL, not user input.
  • Reject empty paths, .., and operational endpoints (health, docs, openapi.json, redoc) unless there is a specific reason.
  • Strip cookies, inbound Authorization, Host, Content-Length, Accept-Encoding, hop-by-hop headers, and Set-Cookie on responses.
  • Set upstream auth server-side.
  • Preserve query strings only after path validation.
  • Keep fine-grained authorization in the upstream service.

Validation checklist

Render Traefik and confirm no accidental public route or port:

tmpdir=$(mktemp -d)
python3 deployment/scripts/python/render_traefik_config.py --output-dir "$tmpdir"
rg -n "<service>|ext/<service>|:<port>" "$tmpdir" && exit 1
rm -rf "$tmpdir"

Validate Portal:

cd platform/portal/ui
npm run lint
npm run build

Validate behavior after deploy:

curl -skI https://portal.desarrolloselectronicos.com/ext/<service>/health
curl -sk https://portal.desarrolloselectronicos.com/ext/<service>/<known-path>

Expected:

  • unauthenticated requests fail with 401 or 403;
  • blocked operational paths return 404;
  • authenticated requests reach the upstream service and enforce upstream roles.

Current mappings

Public pathUpstreamAuth source
/ext/user-service/*http://user-service:8000/api/v1/*Portal session access token