Cloud Services & Operations
The operator's reference for the hosted side of PMS Sync — what runs where, how it deploys, how to check it is healthy, and which environment variables configure it. The hosted Portal and Proxy are multi-tenant: one deployment serves every client, with each tenant isolated by its org config and token claims. All secrets are environment variables resolved from Azure Key Vault — never hard-coded and never shown here.
1. Services, ports & deployment
| Service | Port | Where it runs | Deploy |
|---|---|---|---|
| Import PortalAderantImportPortal | 3060 | pms-sync.alterspective.com.au · staging.pms-sync.alterspective.com.au | Coolify — develop to staging, main to production |
| API ProxyShareDoAderantAPIProxy | 3050 | aderant-proxy.alterspective.com.au | Coolify — main to production |
| SQL API agentAderantSQLAPITypeScript | 4102 | On-premise · via Azure Relay / ngrok | Windows MSI on the client host (not Coolify) |
| Embed widgetfoundry-widgets/pms-sync-embed | — | Sharedo IDE | Foundry CLI → /_ideFiles/Alterspective/PmsSync/Embed |
appuser) and deploy via Coolify: develop feeds the staging portal at staging.pms-sync.alterspective.com.au, while main feeds production. The SQL API agent is installed at the client as a Windows service (see the Deployment Guide). The embed widget is published to the Sharedo IDE with the Foundry CLI — not through Coolify.2. Health & version endpoints
Every service exposes health endpoints (exempt from auth and rate limiting) whose bodies report build/version information — version, buildTime, environment, and the normalized environment label such as PRE PROD for staging. Both backends serve their API under /api/v1/* and stamp an X-API-Version header; the legacy /api/* path remains a deprecated alias.
| Service | Endpoint | Meaning |
|---|---|---|
| Portal | GET /api/health | Liveness (used by the container healthcheck). |
| Proxy | GET /health | Liveness — always 200 when the process is up. |
| Proxy | GET /health/ready | Readiness — checks the upstream on-prem agent. |
| Proxy | GET /health/deep | Full upstream diagnostics with latency. |
| SQL API | GET /api/health | Liveness — always 200; body carries version + schema count. |
| SQL API | GET /api/health/ready | 200 only when SQL is reachable and schema discovered (else 503). |
| SQL API | GET /api/health/checks | Full diagnostics in the body (always 200). |
3. Security controls & rate limits
- API-key guard — every service-to-service call carries
x-api-key; the agent and proxy fail hard in production when the key is missing or weak. - CORS — origins come from
ALLOWED_ORIGINS(comma-separated). Unset in production means deny-all; there are no wildcard defaults. - Rate limits — global ~300 req/min and search ~60 req/min (tunable); import POST routes are fixed at 20 req/min per IP. Traefik sits in front with
trust proxy = 1. - Write gate — imports are blocked unless writes are enabled for the org. The per-org Settings value (
import_write_enabled) takes precedence;IMPORT_WRITE_ENABLEDis the env-level fallback. A blocked import POST returns 501. - Embed SSO — the widget's short-lived HS256 JWT is signed with
PORTAL_EMBED_SECRET(rejected if shorter than 32 chars).
4. Logging & observability
- Structured pino logging in the Portal and Proxy; the SQL API writes structured JSONL (
logs/app-YYYY-MM.jsonl). - A correlation id (
x-correlation-id) propagates Portal → Proxy → SQL API so a single import can be traced across all three. - Sentry error tracking runs in the Portal and Proxy.
- Backend AI capabilities are exposed as MCP services at
/mcp(Keystone OAuth 2.1) — see Architecture & Dependencies.
5. Environment variable reference
Variable names only — values are configured per environment and resolved from Key Vault. These are the key variables for standing up or operating each service; each service's .env.example is the complete, authoritative list.
Import Portal
- KEYSTONE_URL · KEYSTONE_APP_ID — Keystone OIDC identity + app registration
- KEYSTONE_HANDOFF_SECRET · KEYSTONE_SERVICE_KEY — Keystone handoff + service auth
- APP_URL — Public base URL — required; the OIDC callback fails without it
- ENTRA_TENANT_ID · ENTRA_CLIENT_ID · ENTRA_CLIENT_SECRET — Microsoft Entra ID sign-in path (omit to disable the Entra button)
- SESSION_SECRET — Signs the portal session cookie
- SUPABASE_URL · SUPABASE_SERVICE_ROLE_KEY — Per-org config + import history store
- IMPORT_PROXY_URL · IMPORT_PROXY_API_KEY — Reaches the API Proxy
- PORTAL_EMBED_SECRET · PORTAL_AUDIENCE — Verifies the Sharedo embed SSO handoff JWT (shared with the Proxy)
- EMBED_ALLOWED_HOSTS — CSP frame-ancestors + postMessage allowlist for the Sharedo origin (defaults to self — embedding is blocked until set)
- CRON_SECRET — Validates scheduled close-sync calls from the Proxy (must match the Proxy value)
API Proxy
- API_KEY — Service-to-service guard — fails hard in production if absent
- ALLOWED_ORIGINS — CORS whitelist (comma-separated). Unset in prod = deny all. No wildcards.
- RATE_LIMIT_GLOBAL_PER_MIN · RATE_LIMIT_SEARCH_PER_MIN — Rate-limit tuning (defaults 300 / 60)
- DB_PROXY_URL · DB_PROXY_API_KEY — Reaches the on-prem SQL API agent over the tunnel
- ADERANT_API_URL — Upstream Aderant REST base URL (write-back / health probes)
- SHAREDO_IDENTITY_URL · SHAREDO_API_URL · SHAREDO_INSTANCE — Clio Operate / Sharedo write target
- SHAREDO_CLIENT_ID · SHAREDO_CLIENT_SECRET · SHAREDO_SCOPE — OAuth client for the write path
- IMPORT_WRITE_ENABLED — Env-level write fallback — the per-org Settings value takes precedence
- PORTAL_EMBED_SECRET · PORTAL_AUDIENCE — Signs/validates the widget SSO JWT (secret ≥32 chars)
- SUPABASE_URL · SUPABASE_SERVICE_ROLE_KEY — Loads per-org config for embedded pass-through + Proxy MCP
- PORTAL_CRON_URL · CRON_SECRET — Enables scheduled close-sync; disabled unless both set (portal /api/cron/run validates the secret)
- ADERANT_AUTH_TYPE · ADERANT_USERNAME · ADERANT_PASSWORD · ADERANT_DOMAIN — Upstream Aderant REST auth (basic / NTLM)
SQL API agent
- SQL_SERVER · SQL_PORT · SQL_DATABASE · SQL_USER · SQL_PASSWORD — Read-only Aderant connection (see SQL Database Setup)
- SQL_ENCRYPT · SQL_TRUST_SERVER_CERTIFICATE — TLS posture
- PORT — Listen port — default 4102
- API_KEY — x-api-key guard — refuses to start in prod if missing / <32 chars / placeholder
- AZURE_RELAY_CONNECTION_STRING · AZURE_RELAY_NAMESPACE · AZURE_RELAY_PATH · AZURE_RELAY_KEY_NAME · AZURE_RELAY_KEY — Outbound Azure Relay tunnel — the agent enters Hybrid Connection mode only when AZURE_RELAY_CONNECTION_STRING is set
SHAREDO_CLIENT_SECRET, API keys, relay keys, and SQL passwords are environment variables only. IMPORT_WRITE_ENABLED must stay an env var — never hard-coded.For the component model and dependencies, see Architecture & Dependencies. For the on-prem agent install, see the Deployment Guide. For hosting and data residency, see Security & Data Residency.