This guide keeps deployment secrets out of source control, enables API authorisation, and applies the canonical RBAC control plane to supported services.
- You have a deployment environment that can supply secrets and an owner for the access policy.
- You have run
phlo services initso that the shared environment-file layout exists. - You know whether the API should fail open for development (
optional) or fail closed (required). - You have reviewed the regulated-surface boundary in Auth and access.
phlo services init publishes host ports on 127.0.0.1 by default. To expose
them on all interfaces, regenerate with phlo services init --publish-all-interfaces
and protect every exposed service with appropriate network controls and authentication.
This flag affects host publishing only; services continue to listen on their
container networks. Reapply the flag when regenerating with services init.
Put credentials in .phlo/secrets/.env, which is the highest-precedence project environment file and should be mode 0600:
umask 077
cat >> .phlo/secrets/.env <<'EOF'
POSTGRES_PASSWORD=replace-me
MINIO_ROOT_PASSWORD=replace-me
PHLO_ICEBERG_S3_SECRET_KEY=replace-me
EOF
chmod 600 .phlo/secrets/.envThe generated stack receives the values without placing them in phlo.yaml, workflow code, or a tracked file.
Legacy Dagster .phlo/.env and .phlo/.env.local attachments were removed in 0.19.0.
Generating services for a project that still has either file fails and names the migration.
Run phlo services migrate --dry-run to preview the move, then run
phlo services migrate. Defaults move to .phlo/overrides/.env and secrets
move to .phlo/secrets/.env. The old files no longer supply configuration
after migration.
The removal schedule lists the removed compatibility paths and their replacements.
Set the authorisation backend and mode in phlo.yaml:
api:
authorization:
backend: default
mode: requiredYou can instead set authorization under services.phlo-api. The service-specific value takes precedence over top-level api.authorization.
optional permits guarded routes when no backend resolves. required fails closed with HTTP 503 until the configured backend is available.
The default authorisation backend evaluates the canonical project role
assignments and policies. Authentication providers are separate from this backend.
Set provider-specific values in .phlo/secrets/.env and keep the mode declaration in project configuration:
PHLO_AUTHENTICATION_PROVIDER=jwt
PHLO_AUTH_JWT_ISSUER=https://identity.example.com/realms/phlo
PHLO_AUTH_JWT_AUDIENCE=phlo-observatory
PHLO_AUTH_JWT_JWKS_URL=https://identity.example.com/realms/phlo/protocol/openid-connect/certs
PHLO_AUTHORIZATION_MODE=requiredSet authentication.provider: jwt in phlo.yaml too, or omit the YAML provider
when the deployment supplies it through the environment. Nonempty provider
selections must agree; conflicts fail startup. For a private issuer CA, mount its
certificate and set PHLO_AUTH_JWT_CA_FILE to the container path. Configure the
same issuer, client, and trusted CA on oauth2-proxy.
Set PHLO_AUTH_JWT_AUDIENCE to one of the aud values that the issuer puts in
the forwarded access token. This is often the client ID, but not always. A
Cloudflare Access OIDC SaaS application has been observed to set aud to the
OAuth callback URL, such as https://observatory.example.com/oauth2/callback.
Decode a forwarded token to confirm the value for your issuer. If the audience
does not match, sign-in through oauth2-proxy succeeds but phlo-api returns
401 Unauthorized, for example on GET /api/v1/me. phlo-api logs a warning,
authentication_failure with reason_code=invalid_token, that does not name
the audience. The mismatch detail appears only in oidc_token_rejected at debug
level.
The API verifies authentication before authorising the route and environment.
Use the jwt provider for forwarded OIDC bearer tokens. The static provider
and the signed-header proxy provider have different credential contracts;
neither is a substitute for JWT verification in this flow.
Protect Observatory with your authenticated ingress. Its server-side API client forwards
the incoming Authorization: Bearer <credential> header to phlo-api. If that header
is absent or is not a bearer header, the client forwards X-Auth-Request-Access-Token
as a bearer token instead. phlo-api independently verifies that credential and
authorises each operation against the canonical policy and selected environment.
Configure PHLO_API_URL on the Observatory server. Keep the API behind the same
trusted network boundary and configure its authentication provider to verify the
forwarded tokens, including issuer and audience where applicable. Strip
caller-supplied proxy headers at ingress before setting trusted headers.
The generated proxy profile routes observatory.<TRAEFIK_DOMAIN> through the
same oauth2-proxy forward-auth middleware as the API. Its /oauth2/ paths go to
oauth2-proxy, not the UI. For browser sign-in on this host, configure the issuer's
registered redirect URI and OAUTH2_PROXY_REDIRECT_URL to
https://observatory.<TRAEFIK_DOMAIN>/oauth2/callback. The existing API-host
callback remains the default for API-only deployments. Cross-host cookie sharing
is not automatic. Configure it explicitly only for trusted sibling hosts.
When the ingress serves one fixed hostname instead of a wildcard domain, set
OBSERVATORY_HOST to that hostname. The Observatory and /oauth2/ routes then
match it, and the default redirect allow-list includes it. The default
OAUTH2_PROXY_REDIRECT_URL uses the API host over HTTP; when browsers sign in on
the Observatory host, set it to https://<OBSERVATORY_HOST>/oauth2/callback and
register the same URL with the identity provider. Set PHLO_API_HOST
the same way for a dedicated API hostname. Do not give both the same value:
the two routers would then match the same requests. Observatory reaches the API
over the internal network, so a browser-only deployment can leave the API on its
default name.
When a TLS-terminating proxy forwards plain HTTP to Traefik, set
TRAEFIK_TRUSTED_IPS to that proxy's source address as Traefik sees it, for
example TRAEFIK_TRUSTED_IPS=172.20.0.1/32. Traefik then keeps the proxy's
X-Forwarded-Proto: https for oauth2-proxy and the API. By default Traefik
trusts no forwarded headers and replaces them. Traefik builds the sign-in
return URL from its own plain HTTP connection, so also set
OAUTH2_PROXY_SIGN_IN_QUERY=/oauth2/sign_in. oauth2-proxy then returns the
browser to / on the original https origin instead of an http URL.
Terminate HTTPS at the trusted ingress and install the issuer's CA where the proxy and API require it. Do not disable TLS verification. The generated production profile removes direct API and UI host-port mappings while retaining their authenticated ingress routes. Review resolved Compose overrides too; publishing a separate UI port would bypass the browser session gate.
Ordinary phlo services start honours the saved provider and enabled services.
Without a caller session it reports infrastructure startup and
verification_required, not authenticated dashboard readiness. Sign in through
the configured ingress and inspect /api/v1/readiness?env=<configured-id>.
All orchestration and incident checks must be ready; disabled query preview
does not block the dashboard. A local authenticated deployment with loopback
API/UI ports can use its existing PHLO_AUTHZ_TOKEN for the same non-mutating
checks on startup. The token is never saved or forwarded across redirects.
Protected external deployments require verification through their real ingress
session, not a development identity.
There is no shared Observatory token or browser authToken setup. Do not put
service credentials in browser code. An unauthenticated API response remains
HTTP 401, and an authenticated policy denial remains HTTP 403.
For a loopback-only development identity and a single configured target, follow Set up Observatory locally. Do not use that development profile for a network-accessible deployment.
The .phlo/authorization/ directory is the source for roles, policies, and compiled backend artifacts. Validate and preview before synchronising:
phlo authz validate
phlo authz planvalidate checks the policy files, and plan shows intended changes without applying them. The current CLI does not expose authz check or authz explain. Use phlo authz verify after synchronisation to compare backend state with the desired policy.
Render the service settings, then run the production checks without contacting Docker:
phlo services init
phlo services preflight --production
phlo doctorPreflight reports authorisation, authentication, secret-file, and protected-port checks. Doctor then checks project discovery and live service health.
Phlo records request-time authentication and authorisation evidence for its governed surfaces, but it does not secure arbitrary direct ports or implement row-level policies inside Superset. Operators still own TLS termination, identity-provider availability, backend-native grants, credential rotation, log retention, and network controls.
stat -c '%a' .phlo/secrets/.env
phlo authz verifyThe file mode is 600, and verification reports whether backend state matches the desired policy.
- Run in production for deployment preflight and operations.
- Monitor and debug for auth and service failures.
- Auth and access for the decorator and policy model.