This guide covers the full operator runbook for running Nexus as a shared hosted web application on a Windows machine with Docker Desktop + WSL2.
- Windows 10/11 with Docker Desktop installed and WSL2 integration enabled.
- Git for Windows (to clone and pull the repo).
- Node.js 20+ and npm (to build the Nexus static bundle on the host or in CI).
- An Azure app registration for Entra SSO (see Entra app registration).
The worker persists corpus files and job artifacts to a bind-mounted host path.
The default is C:\deepvault-nexus\data\runtime. Create it before first launch:
mkdir C:\deepvault-nexus\data\runtimecd C:\deepvault-nexus
git clone <repo-url> .npm install
npm run buildThis writes the static assets to dist/. Caddy mounts this directory read-only
so you never need to rebuild the Caddy image after a frontend update.
copy .env.example .env
notepad .envFill in at minimum:
| Variable | Required | Notes |
|---|---|---|
BISHOP_PROVIDER |
Yes | openai, gemini, or anthropic |
OPENAI_API_KEY (or equivalent) |
Yes | Key for the chosen provider |
ENTRA_TENANT_ID |
Yes (hosted) | Azure Directory ID |
ENTRA_CLIENT_ID |
Yes (hosted) | Azure Application ID |
WORKER_AUTH_ENABLED |
Yes | Set to true in production |
OPERATOR_ALLOWLIST |
Recommended | Entra object IDs of operators |
NEXUS_RUNTIME_PATH |
Optional | Default: C:\deepvault-nexus\data\runtime |
docker compose up -dBoth containers start in the background. Check status:
docker compose psVerify the worker is healthy:
curl http://localhost/api/healthExpected response: {"status":"ok","version":"..."}.
git pull
npm install
npm run build
# Caddy picks up the new files from the bind mount — no restart needed.git pull
docker compose build worker
docker compose restart workerCaddy keeps serving static files while the worker restarts. The restart typically takes 3–5 seconds.
git pull
npm install
npm run build
docker compose build worker
docker compose up -d --no-deps --build worker- Look up the user's Entra object ID in the Azure portal (Azure Active Directory → Users → select user → Object ID).
- Add the UUID to
OPERATOR_ALLOWLISTin.env(comma-separated). - Restart the worker to reload the allowlist:
docker compose restart workerRemove the UUID from OPERATOR_ALLOWLIST in .env, then restart the worker.
# Worker API health
curl http://localhost/api/health
# Container status
docker compose ps
# Worker logs (last 50 lines)
docker compose logs --tail=50 worker
# Access log (one JSON line per authenticated request)
type C:\deepvault-nexus\data\logs\access-<date>.jsonlTo start Nexus automatically when the machine boots:
Option A — Docker Desktop auto-start (simplest)
Enable "Start Docker Desktop when you log in" in Docker Desktop settings, then
create a scheduled task to run docker compose up -d at login:
$action = New-ScheduledTaskAction -Execute "docker" -Argument "compose up -d" -WorkingDirectory "C:\deepvault-nexus"
$trigger = New-ScheduledTaskTrigger -AtLogOn
Register-ScheduledTask -TaskName "Nexus hosted stack" -Action $action -Trigger $trigger -RunLevel HighestOption B — Windows service via NSSM
Use NSSM to wrap docker compose up as a Windows service
that starts before any user logs in.
- In the Azure portal, go to Azure Active Directory → App registrations → New registration.
- Name:
Nexus(or your preferred display name). - Supported account types: Accounts in this organizational directory only.
- Redirect URI:
http://<your-nexus-hostname>/(orhttp://localhost/for local testing). - After creation, note the Application (client) ID and Directory (tenant) ID.
- Under Expose an API, set the Application ID URI to
api://<client-id>and add a scope namedNexus.Access(admins and users can consent). - Under API permissions, add
api://<client-id>/Nexus.Accessas a delegated permission and grant admin consent. - Set
ENTRA_TENANT_IDandENTRA_CLIENT_IDin.env.
| Symptom | Likely cause | Fix |
|---|---|---|
curl http://localhost/api/health returns 502 |
Worker not yet healthy | Wait 15s and retry; check docker compose logs worker |
| Browser shows blank page | dist/ is empty |
Run npm run build |
| Browser login loop | ENTRA_CLIENT_ID mismatch or redirect URI not registered |
Verify app registration redirect URIs |
| 403 on job-control endpoints | User not in OPERATOR_ALLOWLIST |
Add their Entra object ID to .env and restart worker |
| Access log not written | data/logs/ not writable |
Verify the bind-mount host path exists and Docker Desktop has file system access |