Topology and Environments
Local and production deployment topology, domain mapping, and environment contracts for SterlingIndex.
SterlingIndex is deployed as a split system: independent frontend apps at the edge, and a shared backend runtime that owns data access, auth sessions, background work, and runtime dependencies.
The checked-in topology is defined by two repo-visible layers:
infra/docker-compose.local.ymlfor the current monorepo's local backend and stateful servicesinfra/vps/*for the current production-oriented single-host backend baseline
The repo also keeps a Kubernetes reference layer:
infra/k8s/*for a heavier future multi-node backend shape
Repo-visible secret management now has a third layer:
INFISICAL.mdfor the recommended Infisical project layout, path mapping, local CLI workflow, and Vercel/Cloudflare sync model
Topology Summary
Local development
browser
|- apps/www (Next.js, :3000)
|- apps/dashboard (Vite, :8080)
|- apps/admin-desktop (Electron admin renderer :3001, local API :13000)
|- apps/admin-mcp (stdio MCP server, local API :13000 when started)
|- apps/legacy-admin (legacy Vite reference, :3001)
|- apps/docs (Next.js, :3001 when run separately)
`- services/platform-api API (:8000)
|- Postgres
|- Redis
|- MinIO
|- Ollama
|- mock DROP API
|- Celery worker (`scrape-cloud,validation-publish,compliance`)
`- scheduler tick (`python -m rosterscraper.scheduler_tick`)
optional macOS scrape node
`- Celery worker (`scrape-local`) against cloud/local Postgres, Redis, and R2-compatible storage
Production baseline
www.sterlingindex.com dashboard.sterlingindex.com
docs.sterlingindex.com active admin: macOS Electron
\ | /
\ | /
api.sterlingindex.com
|
Cloudflare proxy and origin cert
|
Vercel / Cloudflare Pages / Contabo VPS
|
nginx + Docker Compose on the VPS
|
local Postgres / Redis / R2 / systemd timer
|
optional macOS scrape-local worker over cloud stateLocal Development Topology
The root workspace script in package.json starts four processes:
pnpm dev:backend->docker compose -f infra/docker-compose.local.yml --env-file infra/.env.local.example up --buildpnpm dev:www->apps/wwwpnpm dev:dashboard->apps/dashboardpnpm dev:admin->apps/admin-desktop
The docs app is intentionally separate and runs with pnpm dev:docs.
The workspace also exposes Infisical-backed equivalents:
pnpm dev:infisicalfor the main local stackpnpm dev:admin:infisicalfor the Electron admin with the/apps/admin-desktopsecret pathpnpm dev:docs:infisicalfor the docs app alone
The old hosted admin is intentionally outside the active admin path. Use pnpm dev:legacy-admin, pnpm build:legacy-admin, or pnpm test:legacy-admin only when you are checking the retained reference app.
The MCP admin server is also outside the default root pnpm dev script. Use pnpm --dir apps/admin-mcp build and configure an MCP client to launch apps/admin-mcp/dist/index.js, or use pnpm --dir apps/admin-mcp start for local development.
Compose-backed services
| Service | Default host port | Purpose | Source of truth |
|---|---|---|---|
postgres | 5432 | Primary relational store for app data | infra/docker-compose.local.yml, infra/.env.local.example |
redis | 6379 | Queue broker and cache | infra/docker-compose.local.yml, infra/.env.local.example |
minio | 9000 (9001 console) | Local object storage for artifacts | infra/docker-compose.local.yml, infra/.env.local.example |
ollama | 11434 | Local-first LLM host for backend workflows | infra/docker-compose.local.yml, infra/.env.local.example |
mock-drop-api | 18081 | Fixture-backed compliance service for local verification | infra/docker-compose.local.yml |
migrate | none | Applies migrations before long-running services start, and optionally seeds demo data | infra/docker-compose.local.yml |
api | 8000 | FastAPI runtime for sessions, bootstrap data, and platform endpoints | infra/docker-compose.local.yml |
worker | none | Celery worker consuming scrape-cloud,validation-publish,compliance queues for the default local stack | infra/docker-compose.local.yml |
scheduler | none | One-shot scheduler tick container that runs recurring dispatch logic and exits | infra/docker-compose.local.yml |
Optional local scrape node
Hybrid staging and production can attach a separate macOS worker outside Compose:
- command baseline:
pnpm --dir services/platform-api dev:worker:local - queue contract: consumes
scrape-localonly - state contract: points at the shared
DATABASE_URL,REDIS_URL,OBJECT_STORAGE_*, andATTORNEY_MEDIA_* - model/browser contract: can use local
OLLAMA_HOST,LMSTUDIO_BASE_URL, and native macOS browser automation without forcing those dependencies onto the hosted API
This worker is optional capacity, not required infrastructure. If it is offline, the hosted platform can continue routing ad hoc runs to scrape-cloud.
Frontends outside Compose
| Surface | Local default | How it is configured | Notes |
|---|---|---|---|
Marketing site (apps/www) | http://localhost:3000/ | apps/www/package.json, apps/www/next.config.ts | Deploys at the root marketing origin and hands authenticated traffic off to the dashboard host. |
Dashboard (apps/dashboard) | http://localhost:8080 | apps/dashboard/vite.config.ts, apps/dashboard/src/main.tsx | Reads VITE_PLATFORM_API_URL, defaulting to http://localhost:8000. |
Admin (apps/admin-desktop) | http://localhost:3001 renderer, http://127.0.0.1:13000 local API | apps/admin-desktop/vite.renderer.config.ts, apps/admin-desktop/src/main/main.ts | Active Electron admin with desktop bridge, local API supervision, and scrape-local worker controls. |
Admin MCP (apps/admin-mcp) | stdio MCP server, http://127.0.0.1:13000 local API when started | apps/admin-mcp/src/index.ts, packages/admin-runtime/src/runtime-manager.ts | Non-visual operator adapter for Codex/Claude-style clients. Connection is inert until a start/repair or ensure_runtime workflow call. |
Legacy admin (apps/legacy-admin) | http://localhost:3001 | apps/legacy-admin/vite.config.ts, apps/legacy-admin/src/components/admin-auth-gate.tsx | Legacy hosted reference app only. |
Docs (apps/docs) | http://localhost:3001/docs | apps/docs/package.json, apps/docs/src/lib/site.ts | Runs independently from the root pnpm dev script. Its default port overlaps the admin app, so only one can keep 3001 without a local override. |
Local environment contract
infra/.env.local.example is the checked-in coordination point for the workspace-level local stack. It defines:
- stateful services:
POSTGRES_*,REDIS_PORT,MINIO_PORT,MINIO_CONSOLE_PORT - backend runtime URLs:
DATABASE_URL,INTEL_DATABASE_URL,CORE_DATABASE_URL,REDIS_URL,OLLAMA_HOST,LMSTUDIO_BASE_URL - orchestration mode and scrape targeting:
ORCHESTRATION_EXECUTION_MODE,SCRAPE_EXECUTION_TARGET_DEFAULT - worker identity and queue advertisement:
SCRAPE_NODE_ROLE,SCRAPE_NODE_LABEL,SCRAPE_NODE_PLATFORM,SCRAPE_NODE_ADVERTISED_QUEUES - public app URLs consumed by the backend:
APP_PUBLIC_WWW_URL,APP_PUBLIC_DASHBOARD_URL,APP_PUBLIC_ADMIN_URL,APP_PUBLIC_API_URL - frontend runtime URLs:
VITE_PLATFORM_API_URL,VITE_CONTROL_PLANE_URL,NEXT_PUBLIC_DASHBOARD_URL,NEXT_PUBLIC_DOCS_BASE_URL - auth/session defaults:
AUTH_PROVIDER,WORKOS_*,CORS_ALLOWED_ORIGINS,SESSION_COOKIE_NAME,SESSION_COOKIE_DOMAIN,SESSION_COOKIE_SECURE - observability inputs:
APP_RELEASE,MONITORING_SENTRY_DSN,MONITORING_SENTRY_TRACES_SAMPLE_RATE,NEXT_PUBLIC_APP_ENV,NEXT_PUBLIC_APP_RELEASE,NEXT_PUBLIC_SENTRY_DSN,NEXT_PUBLIC_SENTRY_TRACES_SAMPLE_RATE,NEXT_PUBLIC_POSTHOG_KEY,NEXT_PUBLIC_POSTHOG_HOST,VITE_APP_ENV,VITE_APP_RELEASE,VITE_SENTRY_DSN,VITE_SENTRY_TRACES_SAMPLE_RATE,VITE_SENTRY_REPLAY_SESSION_SAMPLE_RATE,VITE_SENTRY_REPLAY_ON_ERROR_SAMPLE_RATE,VITE_POSTHOG_KEY,VITE_POSTHOG_HOST - security, storage, and media settings:
OBJECT_STORAGE_*,ATTORNEY_MEDIA_*,VITE_TURNSTILE_SITE_KEY,VITE_OPENFREEMAP_COMPACT_STYLE_URL,VITE_OPENFREEMAP_IMMERSIVE_STYLE_URL,SECURITY_TURNSTILE_SECRET_KEY,PRODUCT_DEMO_SEED_ENABLED
The docs app keeps its own .env.example because it publishes docs-site metadata from NEXT_PUBLIC_DOCS_BASE_URL and the shared dashboard-login handoff from NEXT_PUBLIC_DASHBOARD_URL.
Secrets source of truth
The repo-level recommendation is now:
- use Infisical as the source of truth for local and hosted secrets,
- sync
apps/wwwandapps/docspublic env vars into Vercel, - sync
apps/dashboardenv vars into Cloudflare Pages, - keep active admin env values under the
/apps/admin-desktopInfisical path or import them through the desktop app's env-import workflow, - import Admin MCP env bundles through
sterling_runtime_import_env_bundle; the cache is encrypted under~/Library/Application Support/Sterling Admin MCP/unless plaintext cache is explicitly enabled for tests, - sync
apps/legacy-adminto Cloudflare Pages only if you intentionally deploy the reference web admin, - use Infisical machine identities for the API, worker, and scheduler where possible.
The checked-in operating guide for that model is INFISICAL.md.
Production-oriented Topology
The checked-in deployment runbook now treats infra/vps as the primary production backend path and keeps the frontend apps as independent deployments:
apps/wwwon Vercelapps/docson Vercel fordocs.sterlingindex.comapps/dashboardon Cloudflare Pagesapps/admin-desktopas the packaged Electron control room for operatorsapps/admin-mcpas a local stdio MCP control surface for agent-assisted operatorsapps/legacy-adminretained as a reference Vite app, not a default deploy targetservices/platform-apiplus worker, scheduler, Postgres, and Redis on a single Contabo VPS
VPS baseline
| Asset | Responsibility | Key details |
|---|---|---|
infra/vps/docker-compose.yml | Runtime stack | Runs nginx, api, worker, scheduler, postgres, redis, and a one-shot migrate job. |
infra/vps/nginx/api.sterlingindex.com.conf | Public ingress | Terminates TLS with a Cloudflare Origin Certificate and proxies to the internal api:8000 service. |
infra/vps/systemd/sterlingindex-platform.service | Host orchestration | Boots the full Compose stack under systemd and keeps it alive across reboots. |
infra/vps/systemd/sterlingindex-postgres-backup.timer | Backup schedule | Runs nightly Postgres dumps to Cloudflare R2. |
infra/vps/scripts/bootstrap-host.sh | Host bootstrap | Installs Docker, Compose, ufw, and fail2ban on Ubuntu 24.04. |
Operationally, that means the repo-visible production backend is now:
- Cloudflare proxy in front of
api.sterlingindex.com - Nginx on the VPS listening on
80/443 - FastAPI, worker, scheduler tick loop, Postgres, and Redis on an internal Docker network
- Cloudflare R2 retained for object storage and media
- Infisical machine identity used to inject backend runtime secrets
Kubernetes reference baseline
The Kubernetes manifests remain useful as a future multi-node reference, but they are no longer the default deployment story for the current production path.
| Manifest | Responsibility | Key details |
|---|---|---|
infra/k8s/namespace.yaml | Namespace boundary | Creates the sterlingindex namespace. |
infra/k8s/configmap.example.yaml | Shared non-secret config | Declares public app domains, database URLs, Redis URL, and session cookie settings. |
infra/k8s/configmap.example.yaml (Secret block) | Sensitive config placeholders | Holds WORKOS_*, object storage credentials, MONITORING_SENTRY_DSN, SKILL_SIGNING_KEY, CPPA_API_KEY, and SUPPRESSION_ENCRYPTION_KEY. |
infra/k8s/platform-api.yaml | API deployment and service | Runs uvicorn rosterscraper.api.app:app with 2 replicas and exposes a service on port 80 -> 8000. |
infra/k8s/workers.yaml | Background workloads | Runs a platform-worker deployment with 2 replicas and a minutely platform-scheduler-tick CronJob. |
infra/k8s/ingress.example.yaml | Public ingress | Routes api.sterlingindex.com to the platform-api service and terminates TLS. |
Production domain map
| Surface | Production hostname | Repo evidence |
|---|---|---|
| Marketing site | https://www.sterlingindex.com | infra/k8s/configmap.example.yaml |
| Docs app | https://docs.sterlingindex.com | infra/k8s/configmap.example.yaml, infra/k8s/README.md |
| Dashboard | https://dashboard.sterlingindex.com | infra/k8s/configmap.example.yaml |
| Active admin | Packaged macOS Electron app pointed at the control-plane API | apps/admin-desktop/package.json, apps/admin-desktop/src/main/main.ts, apps/admin-desktop/vite.renderer.config.ts |
| Admin MCP | Local stdio MCP server pointed at the MCP-managed local control-plane API | apps/admin-mcp/package.json, apps/admin-mcp/src/index.ts, packages/admin-runtime/src/runtime-manager.ts |
| Legacy admin reference | https://admin.sterlingindex.com only if manually retained as a hosted reference | infra/k8s/configmap.example.yaml, apps/legacy-admin |
| API | https://api.sterlingindex.com | infra/k8s/configmap.example.yaml, infra/k8s/ingress.example.yaml |
Neither the VPS bundle nor the Kubernetes reference includes manifests for the frontend hosts themselves, so the repo-visible production picture remains "independent frontend deployments pointed at one shared API origin."
The repo now also includes infra/cloudflare/README.md, which is the checked-in runbook for the intended public edge shape:
- proxy
www.sterlingindex.com,dashboard.sterlingindex.com, andapi.sterlingindex.comthrough a single Cloudflare zone - enable the free-plan baseline for DDoS mitigation, Bot Fight Mode, and targeted WAF/rate-limit rules
- keep the
apiorigin proxied to the Contabo VPS behind Nginx and a Cloudflare Origin Certificate - keep origin-specific headers and build-time sourcemap behavior in the app repos, because Cloudflare config itself remains an external deployment concern
At the secret-management layer, hosted frontends are expected to receive environment values through Infisical Secret Syncs rather than ad hoc manual editing inside each platform dashboard.
Shared Environment Variables
The environment contract is split by responsibility rather than by app.
Public origin and redirect settings
| Variable | Used by | Why it matters |
|---|---|---|
APP_PUBLIC_WWW_URL | backend runtime | Canonical marketing host used by the platform runtime. |
APP_PUBLIC_DASHBOARD_URL | backend runtime | Canonical dashboard origin used in auth and link generation. |
APP_PUBLIC_ADMIN_URL | backend runtime | Canonical admin origin for control-plane redirects and links. |
APP_PUBLIC_API_URL | backend runtime | Canonical API origin surfaced to app consumers and callbacks. |
APP_ENV | backend runtime and observability | Aligns backend environment tagging with frontend deploy environments. |
APP_RELEASE | backend runtime and observability | Shared release identifier used to correlate frontend and backend deploys. |
NEXT_PUBLIC_DASHBOARD_URL | apps/www, apps/docs | Public-site login flows link users into the dashboard sign-in handoff. |
NEXT_PUBLIC_APP_ENV | apps/www | Marketing-side environment tagging for Sentry and PostHog. |
NEXT_PUBLIC_APP_RELEASE | apps/www | Marketing-side release tagging and sourcemap naming. |
NEXT_PUBLIC_SENTRY_DSN | apps/www | Enables lean public-site Sentry capture. |
NEXT_PUBLIC_SENTRY_TRACES_SAMPLE_RATE | apps/www | Keeps marketing traces under free-plan limits. |
NEXT_PUBLIC_POSTHOG_KEY | apps/www | Enables anonymous marketing analytics in PostHog. |
NEXT_PUBLIC_POSTHOG_HOST | apps/www | Selects the PostHog ingestion host. |
NEXT_PUBLIC_DOCS_BASE_URL | apps/docs | Sets docs metadata/canonical origin for docs.sterlingindex.com. |
APP_PUBLIC_DOCS_URL | apps/docs | Fallback name supported by apps/docs/src/lib/site.ts. |
VITE_PLATFORM_API_URL | apps/dashboard | Dashboard fetches session and bootstrap data from the shared API. |
VITE_APP_ENV | apps/dashboard, apps/admin-desktop, apps/legacy-admin | Frontend environment tagging for authenticated Vite/Electron-renderer surfaces. |
VITE_APP_RELEASE | apps/dashboard, apps/admin-desktop, apps/legacy-admin | Frontend release tagging and sourcemap naming for authenticated Vite/Electron-renderer surfaces. |
VITE_SENTRY_DSN | apps/dashboard, apps/admin-desktop, apps/legacy-admin | Enables browser-side Sentry capture for authenticated Vite/Electron-renderer surfaces. |
VITE_SENTRY_TRACES_SAMPLE_RATE | apps/dashboard, apps/admin-desktop, apps/legacy-admin | Controls browser trace volume for authenticated Vite/Electron-renderer surfaces. |
VITE_SENTRY_REPLAY_SESSION_SAMPLE_RATE | apps/dashboard | Keeps replay sampling conservative on the free plan. |
VITE_SENTRY_REPLAY_ON_ERROR_SAMPLE_RATE | apps/dashboard | Captures replay context on error while still biasing volume downward. |
VITE_POSTHOG_KEY | apps/dashboard | Enables identified product analytics in PostHog. |
VITE_POSTHOG_HOST | apps/dashboard | Selects the PostHog ingestion host. |
VITE_TURNSTILE_SITE_KEY | apps/dashboard | Reserved frontend site key for future login hardening. |
VITE_OPENFREEMAP_COMPACT_STYLE_URL | apps/dashboard | Optional override for the default OpenFreeMap Positron style used by compact city maps. |
VITE_OPENFREEMAP_IMMERSIVE_STYLE_URL | apps/dashboard | Optional override for the default OpenFreeMap Positron style used by immersive city maps. |
VITE_CONTROL_PLANE_URL | apps/admin-desktop, apps/legacy-admin, and legacy services/platform-api/apps/web | The active desktop renderer defaults to http://127.0.0.1:13000; reference web shells can point at an explicit control-plane API origin. |
STERLING_REPO_ROOT | apps/admin-mcp, packages/admin-runtime | Repo root used by the MCP runtime to find services/platform-api and workspace paths. |
STERLING_MCP_DATA_DIR | apps/admin-mcp, packages/admin-runtime | Optional override for the MCP data, log, and env-cache directory. |
STERLING_MCP_BACKEND_DIR | packages/admin-runtime | Optional override for the backend working directory. Defaults to services/platform-api. |
STERLING_MCP_PYTHON | packages/admin-runtime | Python executable used to start uvicorn and Celery. |
STERLING_MCP_ALLOW_PLAINTEXT_ENV_CACHE | packages/admin-runtime | Test/dev escape hatch for plaintext env cache. Do not enable for real operator use. |
STERLING_MCP_API_HOST, STERLING_MCP_API_PORT | packages/admin-runtime | Managed local API bind target. Defaults to 127.0.0.1:13000. |
MONITORING_SENTRY_DSN | backend runtime | Enables API-side Sentry reporting. |
MONITORING_SENTRY_TRACES_SAMPLE_RATE | backend runtime | Controls API-side trace volume. |
SECURITY_TURNSTILE_SECRET_KEY | backend runtime | Reserved server-side secret for future Turnstile verification. |
State, storage, and runtime dependencies
| Variable group | Used by | Repo-visible default |
|---|---|---|
DATABASE_URL, INTEL_DATABASE_URL, CORE_DATABASE_URL | API, migrations, worker, scheduler tick | Local defaults point at the local Compose postgres container; VPS production points at the host-local postgres service. |
REDIS_URL | API, worker, scheduler tick | Local defaults point at the local Compose redis container; VPS production points at the host-local redis service. |
OBJECT_STORAGE_*, ATTORNEY_MEDIA_* | API, migrations, worker, scheduler tick | Local default targets MinIO; production baseline keeps Cloudflare R2 and public media credentials. |
ORCHESTRATION_EXECUTION_MODE, SCRAPE_EXECUTION_TARGET_DEFAULT | API, worker, scheduler tick | Default local mode is inline; hosted staging/prod can switch to queued orchestration with auto, cloud, or local targeting. |
SCRAPE_NODE_ROLE, SCRAPE_NODE_LABEL, SCRAPE_NODE_PLATFORM, SCRAPE_NODE_ADVERTISED_QUEUES | worker, runtime readiness | Identifies whether a node is the hosted worker or an optional macOS scrape node and which queues it can claim. |
OLLAMA_HOST, LMSTUDIO_BASE_URL, OPENROUTER_API_KEY, OPENAI_COMPATIBLE_* | API, worker, migrations, active admin settings | Internal LLM routing can use local Ollama/LM Studio, OpenRouter, or a bring-your-own OpenAI-compatible endpoint; the admin settings UI keeps these separate from managed execution providers. |
BROWSER_USE_API_KEY, Browser Use Agent settings, browser-use-cloud browser settings | API, worker, active admin settings | Enables Browser Use diagnostics, the remote CDP browser engine, and Browser Use Agent managed execution when explicitly selected. Budget, timeout, proxy, profile, recording, and cached-rerun controls are stored in control-plane settings. |
CPPA_DROP_API_URL, CPPA_MOCK_MODE, CPPA_API_KEY | API and workers | Switch between fixture-backed local compliance flows and live CPPA integrations. |
SKILL_SIGNING_KEY, SUPPRESSION_ENCRYPTION_KEY | API and workers | Signing and encryption keys required by backend flows. |
Session and cross-origin assumptions
- Local Compose defaults use
SESSION_COOKIE_DOMAIN=localhost. - The VPS production baseline switches to
SESSION_COOKIE_DOMAIN=.sterlingindex.comandSESSION_COOKIE_SECURE=trueso one product/auth session can spanwww,dashboard, andapisubdomains. The active Electron admin still uses the control-plane admin session against its configured API origin. services/platform-api/src/rosterscraper/config.pychecks in localhost defaults, butCORS_ALLOWED_ORIGINScan now be supplied as a comma-separated list or JSON array for production origins such aswww,dashboard, andadmin.- In hosted mode, ad hoc discovery and run requests can resolve
execution_target=autotoscrape-localwhen a healthy macOS node is advertising that queue; otherwise they fall back toscrape-cloud. Scheduled jobs stay cloud-oriented by default.
Secret ownership assumptions
- Frontend-only public build vars such as
NEXT_PUBLIC_*andVITE_*should live in app-specific Infisical paths. - Shared backend secrets such as
WORKOS_*,SESSION_COOKIE_*,DATABASE_URL,REDIS_URL, andOBJECT_STORAGE_*should live in shared runtime paths and be imported into API/worker/scheduler scopes. - Build-only sourcemap credentials such as
SENTRY_AUTH_TOKEN,SENTRY_ORG, and project-name overrides should stay separate from runtime secrets so frontend deploys can read them without widening backend access.
Verification and Operational Notes
services/platform-api/scripts/run_local_verification.pytreatsinfra/docker-compose.local.ymlas the shared dependency stack and exports host-sideDATABASE_URL,REDIS_URL,OBJECT_STORAGE_*, and app URL variables before running backend and frontend verification.services/platform-api/docs/local-verification.mddocuments an additional collision-resistant smoke path for the legacy embedded web shell onlocalhost:13000; that shell is useful for backend verification but is not part of the primary monorepo deployment picture.DEPLOY.mdis the current repo-level runbook for the split hosted stack, the VPS backend bundle, the scheduler tick loop, and the optional macOS scrape node that writes directly into the shared cloud state.INFISICAL.mdis the repo-level runbook for the Infisical project shape, path mapping, CLI workflow, and Vercel/Cloudflare Secret Sync setup.infra/cloudflare/README.mdis the repo-visible runbook for Cloudflare proxy, WAF/rate-limit, and Bot Fight Mode rollout acrosswww,dashboard, andapi.infra/vps/README.mdis the checked-in host runbook for Docker Compose, Nginx, systemd, Cloudflare Origin Certificates, and nightly Postgres backups to R2.- The docs site itself is verified with the workspace-level scripts:
pnpm build:docs,pnpm lint:docs, andpnpm typecheck:docs.