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, andSet-Cookieon 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
401or403; - blocked operational paths return
404; - authenticated requests reach the upstream service and enforce upstream roles.
Current mappings
| Public path | Upstream | Auth source |
|---|---|---|
/ext/user-service/* | http://user-service:8000/api/v1/* | Portal session access token |