Domains and Exposure
Wallets, verifiers, and relying parties reach Procivis One from outside your network. Core publishes URLs for them to call: credential offers, proof requests, DIDs, schemas, revocation lists, and published trust lists all point back to Core's base URL. If those URLs are not reachable from a phone or a remote service, issuance and verification fail.
For testing and development, any reachable URL works. For production, set your domain before you issue or publish anything you intend to keep. Changing Core's base URL later breaks the URLs embedded in what you have already issued or published.
Where to start
- Running on localhost and testing with a wallet: see Local testing with a tunnel.
- Moving a localhost deployment to a domain: see Moving from localhost to a domain.
- Running behind your own reverse proxy or load balancer: see Existing reverse proxy or load balancer.
- Changing or checking a domain-related setting: see URL settings.
Public and internal services
| Service | Hostname | Reachable from | Called by |
|---|---|---|---|
| Frontend and BFF, via oauth2_proxy | <domain> | Internet | Operators' browsers |
| Keycloak | keycloak.<domain> | Internet | Operators' browsers, during sign-in |
| Core | core.<domain> | Internet | Wallets and verifiers |
| Bridge | bridge.<domain> | Internet | OIDC relying parties |
| WRPR | wrpr.<domain> | Internet | Wallets, certificate authorities |
| Databases, Redis | — | Internal only | Procivis One services |
Core serves both public protocol endpoints (/ssi, /.well-known) and
management endpoints (/api) on the same hostname. To keep management
endpoints off the public internet, see
API Endpoint Zones. Core's /health and
/metrics endpoints are for internal use and should not be routed
publicly.
To serve different domains, for example one per ecosystem or customer, run separate Core instances, each with its own base URL.
URL settings
Each section below covers one URL and lists every setting that must match it. The Quick start location column shows where each setting lives in the Full Stack Quick Start bundle. If you have reorganized your configuration, the setting names stay the same.
Core settings are shown as YAML paths, followed by the environment variable that overrides them. See Core configuration layers for details of configuration merging.
Core public URL
The URL wallets and verifiers use to reach Core. On a domain:
https://core.<domain>.
| Service | Setting | Quick start location |
|---|---|---|
| Core | app.coreBaseUrl (ONE_app__coreBaseUrl) | env/.core.env |
Desk URL
The URL operators use to reach the Desk. On a domain: https://<domain>.
| Service | Setting | Quick start location |
|---|---|---|
| BFF | frontend.url | env/.bff.env |
| BFF | sts.token.iss | env/.bff.env |
| oauth2_proxy | redirect_url (<Desk URL>/oauth2/callback) | conf/oauth2_proxy/oauth2-proxy.cfg |
| Keycloak | login-client redirect URIs, web origins, post-logout redirect URIs | master-realm.json |
Identity provider URLs
The URLs services use to sign users in and obtain or validate tokens. For
the starter Keycloak on a domain, these start with
https://keycloak.<domain>/realms/master. When you connect your own
identity provider, use its URLs instead.
| Service | Setting | Quick start location |
|---|---|---|
| BFF | sts.iam.tokenValidation.iss | env/.bff.env |
| BFF | sts.iam.tokenValidation.jwksUri | env/.bff.env |
| BFF | auth.technicalUserCredentials.iamTokenEndpointUrl | env/.bff.env |
| BFF | frontend.iamLink | env/.bff.env |
| Bridge | spring.security.oauth2.client.provider.keycloak.token-uri | env/.bridge.env |
| WRPR | spring.security.oauth2.client.provider.keycloak.token-uri | env/.wrpr.env |
| oauth2_proxy | issuerURL | conf/oauth2_proxy/alpha-config.yml |
| oauth2_proxy | backendLogoutURL | conf/oauth2_proxy/alpha-config.yml |
| Keycloak (starter only) | KEYCLOAK_HOSTNAME | env/.keycloak.env |
Service-to-service URLs
The URLs the BFF, Bridge, and WRPR use to reach other Procivis One services.
| Service | Setting | Quick start location |
|---|---|---|
| BFF | core.api.url, bridge.api.url, wrpr.api.url | env/.bff.env |
| Bridge | core.api.url | env/.bridge.env |
| WRPR | core.api.url | env/.wrpr.env |
These can point to either the public hostname, such as
https://core.<domain>, or the internal service name, such as
http://core:3000. The localhost bundle uses internal names; the domain
bundle uses public hostnames.
Routing and certificates
The hostnames your reverse proxy routes and the domains oauth2_proxy accepts.
| Service | Setting | Quick start location |
|---|---|---|
| Traefik | Host() rules for each public service | docker-compose.yml |
| Traefik | acme.email (Let's Encrypt account) | docker-compose.yml |
| oauth2_proxy | whitelist_domains | conf/oauth2_proxy/oauth2-proxy.cfg |
Local testing with a tunnel
On localhost, Core's base URL is http://localhost:3000, which a mobile
wallet cannot reach. To test wallet flows without a domain, expose Core
through a tunnel such as ngrok or Cloudflare Tunnel.
A tunnel makes all of Core reachable from the internet, including the
/api management endpoints. These are protected only by the starter
static token, which every installation shares. Replace the starter
tokens before you start a tunnel.
-
Start a tunnel to port 3000. Use a stable hostname: if the tunnel URL changes on restart, anything issued under the previous URL stops working. For example, with ngrok and a static domain (one included in ngrok free accounts, shown in the ngrok dashboard):
ngrok http 3000 _-url https://<your-static-domain> -
Set the tunnel URL as Core's base URL in
env/.core.env:ONE_app__coreBaseUrl="https://<your-static-domain>" -
Apply the change:
docker compose up -dCompose recreates the services whose configuration changed.
Once the tunnel is set up, wallets can receive issuance and read your verification requests.
Only Core needs a tunnel for issuance and verification. Operator sign-in
and the Desk continue to work on http://localhost.
Moving from localhost to a domain
The simplest path is a fresh domain deployment, following Option B in the
Full Stack Quick Start. Anything issued or
published under localhost URLs is not valid on the new domain, so there
is little to carry over.
If you changed any starter secrets or keys in your localhost deployment, copy those changes into the domain bundle before starting.
Keeping existing Keycloak data
The starter realm is imported only on first start. If you reuse an
existing Keycloak database instead of starting fresh, the realm keeps its
localhost URLs. Update them in the Keycloak admin console:
-
Sign in at
https://keycloak.<domain>/adminwith the bootstrap admin credentials (default:admin/admin). -
Open the
login-clientclient and set:Setting Value Valid redirect URIs https://<domain>/oauth2/callbackValid post logout redirect URIs https://<domain>Web origins https://<domain> -
Change the admin password. The default is shared by every installation.
Existing reverse proxy or load balancer
The domain bundle includes Traefik, which terminates TLS and requests certificates from Let's Encrypt. If your server already runs a reverse proxy, or sits behind a load balancer that terminates TLS, use that instead:
-
Remove the
traefikservice and thetraefik.*labels fromdocker-compose.yml. -
Publish the public services' ports to the host. Bind them to
127.0.0.1if your proxy runs on the same server:oauth2_proxy:ports:- "127.0.0.1:4180:4180"core:ports:- "127.0.0.1:3000:3000"keycloak:ports:- "127.0.0.1:8080:8080"bridge:ports:- "127.0.0.1:9000:9000"wrpr:ports:- "127.0.0.1:9001:9001" -
Route each hostname to its port:
Hostname Upstream <domain>oauth2_proxy, port 4180core.<domain>core, port 3000keycloak.<domain>keycloak, port 8080bridge.<domain>bridge, port 9000wrpr.<domain>wrpr, port 9001If you change a service's listening port, such as Core's
app.serverPort, update its upstream to match. -
Forward the
X-Forwarded-Proto,X-Forwarded-Host, andX-Forwarded-Forheaders. Services use them to generate correcthttpsURLs behind the proxy.
Remove the Traefik settings from Routing and certificates; all other URL settings stay the same.